assign NSMutableArray with #property and 'copy' attribute - objective-c

I'm following an official tutorial Your second iOS App:Storyboard and it told me to declare a property masterBirdSightingList like this(just a specific example and not necessary to know the context) :
#property (nonatomic, copy) NSMutableArray *masterBirdSightingList;
Note that there's an attribute copy. and then synthesize this property :
#synthesize masterBirdSightingList = _masterBirdSightingList;
And next there's one init method which made me confused :
- (void)initializeDefaultDataList {
NSMutableArray *sightingList = [[NSMutableArray alloc] init];
self.masterBirdSightingList = sightingList;
[self addBirdSightingWithName:#"Pigeon" location:#"Everywhere"];
}
Definitely sightingList is allocated for spaces and then it's assigned to the masterBirdSightingList property. The property has a copy attribute, though. it means the instance variable _masterBirdSightingList would be allocated for another space to preserve stuffs from sightingList. Why? Why not directly allocate space for the property like this :
self.masterBirdSightingList = [[NSMutableArray alloc] init];

In Objective-C, the copy attribute in a property means the setter synthesized will look like this:
-(void)setMasterBirdSightingList:(NSMutableArray*)newValue
{
if (_masterBirdSightingList == newValue) return;
// NSMutableArray* oldValue = _masterBirdSightingList;
_masterBirdSightingList = [newValue copy];
// [oldValue release]; // <-- not applicable in ARC.
}
and that dot syntax will always be translated to
[self setMasterBirdSightingList:sightingList];
regardless of the attribute of the property.
The "allocated for another space to preserve stuffs from sightingList" stuff is done via the -copy method. The way you pass the argument to the setter's newValue parameter is irrelevant.
Edit: As #David mentioned in the comment, the -copy method of a mutable type returns an immutable object. You have to override the setter to call -mutableCopy instead. See What's the best way to use Obj-C 2.0 Properties with mutable objects, such as NSMutableArray?.

Related

Does an object initialize automatically if it is the synthesized property of another object?

When you have one object as a property of another object in Objective-C, does it automatically initialize when you use #synthesize?
does it automatically initialize when you use #synthesize?
Yes, it is initialized to nil (no actual object is allocated, however - this is pointer initialization in the C sense of the word, the init method is not called).
By the way, you don't even have to #synthesize to achieve this behavior - every instance variable, even those which don't have a corresponding #property, are automatically initialized either to nil (in case of objects), NULL (in case of other pointers) or 0 (in case of integers and floating-point numbers) by the Objective-C runtime.
Let's try it:
#interface TypicalObject : NSObject
#property (nonatomic) NSNumber *numberProperty;
#end
#implementation TypicalObject
#synthesize numberProperty;
#end
...
TypicalObject *object = [[TypicalObject alloc] init];
NSLog(#"object.numberProperty = %#", object.numberProperty);
The log statement yields:
object.numberProperty = (null)
So, no, properties do not auto-instantiate. All object instance variables begin as nil, however.
No. The #synthesize does not know how to initialize it. Simple -init?
You can allocate and initialize it in the -init… of the referring object.
You still have to init. Try using lazy initialization:
-(MyPropertyClass*)propertyName {
if(!propertyIvarName) {
_propertyIvarName = [[MyPropertyClass alloc] init];
}
return propertyIvarName;
}
or init the property in viewdidload

How does adding a variable as a property affect it?

What difference does it make in memory management to define a variable as a property? For instance:
#interface foo {
NSString *theStr;
}
#end
#implementation foo
- (void)bar {
NSLog(theStr);
}
#end
Versus:
#interface foo {
NSString *theStr;
}
#property(retain) NSString *theStr;
#end
#implementation foo
#synthesize theStr;
- (void)bar {
NSLog(theStr);
}
#end
It seems like the first is autoreleased or something similar, while the second is retained throughout the life of the class. Is that the case, or what is the difference?
If you define a variable just in the interface without defining it as a property (as in your first example) means that you'll have to take care of everything related to memory management yourself. Assigning something to that variable will not retain it automatically, not will setting the variable to something else release the previous value.
Defining it as a property creates getter and setter methods under the hood. Most importantly, if you use it with the "retain" keyword, your setter method will retain the new value (and release the old one if there was one).
Note that the setter method will only be invoked if you use the dot notation, e.g., self.myStr = #"new string", or the method call, e.g., [self setMyStr:#"new string"]. If you just call myStr = #"new string" the setter method will not be called and you need to release the old value yourself and retain the new one.
I don't think the first case shows an autoreleased object, it would all depend on how you managed the creation and the destruction of that particular object. If for instance when you create that object you call:
//This string will indeed be autoreleased
theStr=[NSString stringWithString:#"Jibber jabber"];
//Or even
theStr=#"Jibber jabber";
But you have to take charge of the memory management if you create it in the following way:
//Manage my memory
theStr=[[NSString alloc] init];
//You have to release this property on the dealloc method
-(void)dealloc{
[theStr release];
[super dealloc];
}
On your second example, you create a setter and a getter method for the property theStr and by adding the nonatomic attribute, you make your property not thread safety, meaning that a thread can begin to modify your property while another one is already editing it. And by setting the retain attribute to your property, the setter method will be synthesized the following way:
- (void) setTheStr:(NSString *) newString {
[newString retain];
[theStr release];
theStr = newSupervisor;
}
You can consult more about this in one of my favorite books, Learning Objective-C 2.0 in chapter 12.

Clarification on when to release pointers after allocating

In my last question (here), I had an issue where I was getting an EXC_BAD_ACCESS because I was releasing the variable I had just allocated:
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
should have been
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
However, stack is a retained property of my class. It's declared like so:
#interface StateStack ()
#property (nonatomic, retain) NSMutableArray* stack;
#end
I was under the impression that when you assign a 'retain' variable, it automatically increments the retainCount of the object. So you are supposed to start by releasing your pointer (as here).
Why are these two cases different? Thanks!
Because you had to assign the property, not the instance variable. When you assign to the property it's going to retain the variable again and then you're not going to have the issue. Here's how your code should have been:
NSMutableArray* s = [[NSMutableArray alloc] init];
self.stack = s;
[s release];
This way you're not assigning to the variable, but using the property (that's, in fact, a method). If you did not release in this case then you'd have a memory leak in your code.
When you did stack = s you assigned directly to the instance variable and the array was never retained.
There is no such thing as a "retain variable". It's a retain property — meaning the setter method behind the property retains the new value and releases the old one. But assigning to a variable just assigns. In fact, the reason people generally recommend assigning directly to the instance variable in init is specifically so that it doesn't go through the setter, because the setter could conceivably have side effects you don't want in init (when your object isn't fully constructed yet).
Note: I'm talking about normal memory-management rules here. This is all different if you're using ARC. But I assume you would have mentioned if you were.
self.stack and stack are two completely different things. When you use stack, you are accessing an instance variable, not a property. This means that your accessor methods aren't called, which means automatic memory management isn't used. This is why you shouldn't release s in your example.
If you used self.stack instead, then you would be using a property. The compiler will treat self.stack = value exactly the same as [self setStack:value], and self.stack the same as [self stack]. Since accessors are being used, memory management will be taken care of to match the way you defined your property, and you should release a value after assigning it.
Maurício has the right answer: be sure to assign to the property to gain the benefits of #property. To clarify the point somewhat, try using code like this:
#interface StateStack : NSObject {
NSArray *_stack;
}
#property (nonatomic,retain) NSMutableArray *stack;
#end
#implementation StateStack
#synthesize stack=_stack;
#end
Now, if you try:
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
You'll get an error, which will mean you tried to set the ivar rather than the property as intended. This mismatch between ivar name and property name is against Apple's recommendations, but it's a fine way to help you develop the habit of using property assignment when you intend to do so.

'initializing' a property which is retained

In the iPhone objective-c world, I've seen this pattern everywhere and I use it myself all the time without really understanding what is going on:
In Test.h
#interface Test: UIViewController
{
NSMutableArray *testArray;
}
#property (retain, nonatomic) NSMutableArray *testArray;
And in Test.m
#implementation Test
#synthesize testArray
- (void) viewDidLoad
{
// why do we do this?
NSMutableArray *init = [[NSMutableArray alloc] init];
self.testArray = init;
[init release];
[self.testArray addObject: #"A"]; // why can't I do this directly?
...
}
- (void) dealloc
{
[testArray release];
[super dealloc];
}
My question is: if testArray has a retain on it when it's declared in the property, why do we need to create a new NSMutableArray init object, assign that to testArray and release? Why can't I just start using testArray in viewDidLoad without doing anything else?
I know there's some debate over the best way of doing this (creating a new object, or using an autorelease object), but in both cases, we end up with testArray with a retain count of 1. Which I believe the 'retain' property already gives it. So why the need to create this init object?
The 'retain' property doesn't automatically create an NSMutableArray for you. Rather, it simply indicates that whenever you do assign something to that property, it will be retained.
If your code were this:
- (void) viewDidLoad
{
[self.testArray addObject: #"A"];
}
Then self.testArray would be nil, and thus it would be essentially a no-op. Until you assign something to self.testArray, it's empty.
Here's what's going on.
- (void) viewDidLoad
{
// we need to assign an NSMutableArray to self.testArray.
NSMutableArray *init = [[NSMutableArray alloc] init];
// The array has been retained once (by the call to |alloc|)
self.testArray = init;
// The array is assigned to a property with the 'retain' attribute
// Thus, the array has now been retained twice
[init release];
// We release the array, so it now is retained once.
// We now have an array in self.testArray, so we can add something to it.
[self.testArray addObject: #"A"];
}
The "retain" in the #property directive specifies that the setter should retain the input value instead of simply copying the value. It has nothing to do with allocating (setting aside memory) and initializing (constructing the object) the object. retain on the #property directive simply increments the retain count when the setter is called (which alllows you to do something like self.myobject = something without specifically calling retain.

Releasing a property (Objective-C)

I have a #property which is defined like this :
#property (nonatomic, retain) name;
In a function I parse some xml and set the name property.
My question is should I explicitly release previous retained instance before retain a new one ?
For exemple :
myObj.name = [otherObj getName]; // retain count +1
..
myObj.name = [otherObj getName]; // which is a new instance of a name, is the previous name released ?
In a synthesized property setter, the code does something roughly analogous to this (we'll have name be the property name, and _name will be the instance variable to which it refers):
- (void) setName: (NSString *) newValue
{
if ( newValue == _name )
return;
[newValue retain];
[_name release];
_name = newValue;
}
Now, that shows what would happen based on your property declaration above. Depending on the other possible attributes of that property, that function would either use [newValue copy] instead of [newValue retain] (if the 'copy' attribute was specified), or it would all be wrapped in an #synchronized(self) block (if the 'nonatomic' attribute was NOT supplied).
I should also note that since your property refers to an NSString, which implements the NSCopying protocol, you should really be using copy rather than retain, i.e.:
#property (nonatomic, copy) NSString * name;
That will cause the synthesized setter to use -copy instead of -retain, which is safer when you're actually passed an NSMutableString object. An immutable string would ultimately only get retained by the -copy call, while a mutable string would create a new immutable copy. Failing to do that means that the string itself could be changed after it's been passed into the setter, changing the value without your object's knowledge.
If you have synthesized the accessors for your property (with the #synthesize directive), they will take care of releasing the ivar that is backing the property when the property gets reassigned. The only time you might want to explicitly release the ivar is when your object gets deallocated. So, your dealloc method might look like this:
- (void)dealloc {
[name release];
// other cleanup here
[super dealloc];
}