#implementation GroupedInexedViewController
{
NSDictionary *names;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
names = dict;
[dict release];
}
Is deallocating 'dict' affects 'names'? I mean does it deallocate 'names' too? I saw in another post that its a bad practice? But why?
Edit: ARC is disabled.
In short, if you are not using ARC, yes: deallocating dict will affect names. This is because you are assigning the names pointer to the single NSDictionary you have allocated.
If you wanted to have names retain the NSDictionary when you dealloc dict, you would need to send dict a retain message:
names = [dict retain];
Since you're manually calling release, I'm going to assume you're not using Automatic Reference Counting (ARC).
There is some terminology mix up here.
It doesn't deallocate names. You're decrementing the reference count of dict when you call release. Once that reference count hits 0, the memory will be deallocated.
The problem is you assigned dict to names without first calling retain on dict.
Retaining an object increases it's reference count.
You can either choose to make *names a property, which will handle the memory management for you, or you can manually increment the reference count by calling retain: names = [dict retain];
If you do this, you must also implement a dealloc method and release names inside the dealloc method.
Your code assigns names with the value of dict. dict is a pointer to an object, so when you assign its value to another pointer (names), both pointers are referencing the same object and can be considered identical.
So yes, when you release dict, you are also releasing names.
BTW, you can assign to names directly without going through dict:
names = [[NSDictionary alloc] initWithContentsOfFile:path];
And if you can enable ARC, you never need to worry about releasing objects.
No answer so far seems to mention the difference between ARC and non-ARC (MRC) usage and the difference between properties and instance variables.
First of all, properties are really just setter and getter methods backed by an instance variable. When you set a property like self.dict = someObject; and the property was declared as strong or retain, then someObject is retained.
However, if you only have an instance variable (not a property) and you're using MRC, then merely writing dict = someObject; duplicates the pointer only but does not increase the reference count - if you write [someObject release] after this, you should assume that dict is invalidated as well (even if the object pointed to by these two pointers is not actually deallocated - this is a rule of reference counting).
If you are using ARC, then assigning to a variable increases the reference count by one as well - so if someObject has a reference count of 1, then writing dict = someObject; will increase the reference count of the object (now pointed to both by dict and someObject) to 2.
dict and names are two different variables, do not mix them!!!
"release" only when you alloc, copy, mutablecopy, retain.
Otherwise if gets created in autorelease mode.
And if you are using ARC, then no need of "release" compiler will take care of all these newly allocated spaces.
Related
If I've created an NSArray in the init of an object.
Then later on I want to recreate a new NSArray to the same property should I set the old one to nil first?
i.e.
is it ok to just go...
self.arrayProperty = [[NSArray alloc] init];
or should I do...
self.arrayProperty = nil;
self.arrayProperty = [[NSArray alloc] init];
(I'm just using an array for the sake of this example but it's a general questions about properties).
If it makes any difference, it's a strong property.
The first approach is fine, you don't need to set it explicitly to nil before assigning a new object, since the setter releases the backing object of the property before retaining and assigning the new one. Just what you would do under MRC (except that here you don't autorelease the object).
It's just the same, as with ARC an object dies when there are no more references to it. The only difference is that in the second code you're doing a useless extra operation.
In my app, the singleton class (SharedData) allocates memory for a NSMutableArray:
[self sharedMutableArray] = [[NSMutableArray alloc] init];
Class A populates the this sharedMutableArray:
NSObject *obj = [NSObject alloc] init];
[sharedMutableArray addObject];
obj = nil;
Class B does this - and that's my question:
NSMutableArray *tmpArray = sharedMutableArray;
... uses the tmpArray locally
[tmpArray removeAllObjects];
tmpArray = nil;
This is an inherited code and my hunch is that this is a NO-NO. Can some one confirm that assigning nil to tmpArray will release memory for sharedMutableArray also.... I guess the author wanted to release tmpArray only...
Assigning nil to tmpArray only sets your pointer to the object to nil. It does not affect the object itself (or its lifecycle) at all. In this case, setting the objects you've created to nil does nothing, since their variable declaration is in local scope - if you want the objects to be deallocated from memory you need to send them release before setting the pointer to the object to nil.
However, sending removeAllObjects is affecting your original sharedArray, because you didn't copy the array, you simply set a new pointer to point to the 'singleton'. You probably want this:
NSMutableArray *tmpArray = [NSMutableArray arrayWithArray:sharedMutableArray];
You won't need to use removeAllObjects in the above case because it will be autorelease'd. I suggest you read this.
tmpArray is a pointer, and it's initialized to point to the same mutable array that sharedMutableArray points to. For that reason, the line:
[tmpArray removeAllObjects];
will empty out the array, and anyone using sharedMutableArray will see that change. In other words, the assignment
NSMutableArray *tmpArray = sharedMutableArray;
doesn't make a copy of the array itself, it only copies the pointer. Any messages you send using that pointer will go to the shared array. Likewise, assigning nil to tmpArray sets the pointer tmpArray, but doesn't do anything to the array itself.
Finally, setting a variable to nil never releases memory. Setting a property to nil, on the other hand, will release memory under some conditions (e.g. when the property is declared to retain its contents). You're setting a variable here, not a property, so there's no chance that the array will be released.
If I set a NSString as property
#property (copy) NSString* name;
I always want to use (copy), so that if its value change, all object with such string still have the old value (as specifiedhere).
However, what happens if my NSString is not a class property, but it is just declared in the code ? Is in that case retained or copied everytime I assign a new value ?
thanks
It depends on how you declare it. You should read the documentation on memory management. Basically the rules are:
NSString *aString = [NSString stringWithString:#"Hello"];
NSString *bString = [NSString stringWithFormat:#"%#", #"Hello"];
In these cases, the string is not copied or retained. It is autoreleased, which means it will be automatically deallocated the next time the autorelease pool drains. You do not have to call the release method on them. (So assigning a new value will not cause it to leak.)
NSString *cString = [[NSString alloc] initWithFormat:#"%#", #"Hello"];
[cString release];
According to Objective C convention, methods that use alloc and have a retain count of one are not autoreleased, so you need to release them explicitly. Assigning a new value without releasing the old one will cause a leak.
You can also explicitly call a "copy" method or a "retain" method on a string. In either case, the new string will have a retain count of 1 and will not be autoreleased, so you will need to call the release method on it before you assign a new value.
NSString *dString = [cString retain];
NSString *eString = [cString copy];
...
[dString release];
[eString release];
If it is a property, and you use self.variableName, this will be taken care of for you (through the getters and setters which are generated with #synthesize). If you do it explicitly, you must make sure to call release on variables that you have called retain or copy on.
Edit: As some commentators below have noted, thinking about management in terms of "ownership" is usually the preferred of describing these ideas, rather than retain count.
If it's not a property and just declared in code, you need to explicitly retain or copy it, ie
NSString myString = [otherString copy];
or
NSString myString = [otherString retain];
Either way you also need to ensure it's released at somepoint.
If you're not using the property's setter, like self.name = #"foo" or [self setName:#"foo"], but rather assign the variable directly, like name = #"foo", it doesn't matter at all how the property is declared.
You have to understand that the property syntax is just a shortcut for writing accessor methods (-name and -setName: in this case). If you're not calling these methods (implicitly by setting the property), it doesn't matter how they work internally (which is what you specify by retain or copy).
I know this has been discussed ad-nauseum but I just don't get some of the memory management. I understand that retain keeps the object alive and copy gives one a separate copy of an object.
What I don't get is when it comes to ivars & properties, retain as a property means that the setter releases the old value & retains the new:
property = newValue;
// retain
if (property != newValue)
{
[property release];
property = [newValue retain];
}
but I've seen examples where they assign static strings to the retain property ivars e.g.
self.stringProperty = #"something";
(some other code)
self.stringProperty = #"somethingElse";
The second call to set string should call release on the static string which is not allowed, why doesn't the program crash?
Also, if an object is declared with the retain property & then is assigned something with init, e.g.
#property(retain)someArray;
someArray = [[NSArray alloc] initWithArray:arbArray];
does that mean someArray now has a retain count of 2 but if it was created with
someArray = [NSArray arrayWithArray:arbArray];
the retain count is only 1 because the 2nd is a factory method?
The second call to set string should call release on the static string which is not allowed, why doesn't the program crash?
You can pass release to a constant string, it just won't do anything meaningful, so those lines of code are valid.
does that mean someArray now has a retain count of 2 but if it was created with...the retain count is only 1 because the 2nd is a factory method?
Well, first of all,
someArray = [[NSArray alloc] initWithArray:arbArray];
doesn't use the methods created by #property, it just accesses the ivar directly. To use the property methods, you'd have to use self.someArray = ...;.
But yes,
[[NSArray alloc] initWithArray:arbArray]
returns an object with an effective retain count of 1, and
[NSArray arrayWithArray:arbArray]
returns an object with an effective retain count of 0, so if you did pass them through the "retain" setter created by #property, the ivar would have an effective retain count of 2 and 1, respectively.
This is more that one question, but anyway...
Static strings are special cases in a number of ways, one of which is that you can retain and release them to your heart's content without it having any effect.
As an aside, NString properties often have copy rather than retain semantics, which would anyway obviate that question if it mattered. But it doesn't.
In your second case, assigning to a retain property directly from an alloc (or copy or other ownership-granting call) is bad practice and will leak unless you actively add a corresponding release afterwards, or autorelease during, eg:
self.someArray = [[[NSArray alloc] initWithArray:arbArray] autorelease];
But there's really no reason not to use the class method in this particular case.
The second call to set string should call release on the static string which is not allowed, why doesn't the program crash?
It's not a static string, it's a constant string. However, that is irrelevant to the question, but actually you are allowed to send -retain to any Objective-C object derived from NSObject except NSAutoreleasePool. If you look at the retainCount (a bit naughty, but since we are discussing implementation, OK) of a constant NSString e.g.
NSLog(#"retain count = %u", [#"foo" retainCount]);
you'll most likely find it's set to a really big number (UINT_MAX in fact). This is a signal to the run time to ignore calls to release and retain.
By the way, forgetting to release objects won't crash the program straight away. In fact, if you have lots of RAM, you might not notice until the OS starts swapping.
does that mean someArray now has a retain count of 2 but if it was created with
No, because you didn't use the property to assign the new array, you went straight to the ivar:
self.someArray = [[NSArray alloc] initWithArray:arbArray];
would be a leak.
self.someArray = [NSArray arrayWithArray:arbArray];
would be OK.
I understand that any init... method initializes a new object and that NSString stringWithString makes a copy of the parameter string as a new object. I also understand that being the objects' owner, I can control the release/deallocation of any objects that I allocate. What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
The "Programming in Objective C" book by Kochan (1st ed) uses the following code (see pages 342-344) to explain that the initWithString is preferable to stringWithString because the AddressCard class would own the name variable contents. Also, I don't get any errors making repeated calls to the setName version with the stringWithString method. TIA!!
//header file has appropriate declarations but not included here:
#import "AddressCard.h"
#implementation AddressCard;
-(NSString *) name
{
return name;
}
//Recommended code:
-(void) setName: (NSString *) theName
{
[name release]
name = [[NSString alloc] initWthString: theName];
}
//Incorrect code according to Kochan:
-(void) setName: (NSString *) theName
{
[name release]
name = [NSString stringWthString: theName];
}
//rest of class implementation code snipped
#end
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
What? No.
The rules are simple:
Any object returned by alloc, copy, copyWithZone, or new has a retain count of 1.
retain increases the receiving object's retain count.
release decreases the receiving object's retain count.
autorelease tells the current autorelease pool to send the receiving object the release message “later”.
Any factory method that doesn't have “new” or “copy” in the name (e.g., stringWithString:) returns an object that it has autoreleased on your behalf.
Or, digested a bit:
Any method whose name contains copy, alloc, retain, or new returns an object that you own.
Any method that doesn't, returns an object that you don't own.
To own an object, retain it.
The incorrect implementation of setName: that you show is incorrect because it stores an autoreleased object in an instance variable, when you mean to own the object. You should retain it or, in this case, copy it. One way is to simply use alloc and initWithString:, as in the correct example you show; the other way would be copy.
The Memory Management Programming Guide for Cocoa explains everything. Every Cocoa or Cocoa Touch programmer should read or re-read it from time to time.
Actually, both setters are wrong. The 'incorrect' one is wrong for general memory management reasons (which are well-expounded elsewhere). The 'recommended' one is wrong for 2 reasons:
if (theName == name), then you're
likely to deallocate your object in
the first line, and then attempt to
use the deallocated object as a
parameter to -initWithString: on the
second line, resulting in undefined
behavior.
-initWithString: does not handle being passed nil gracefully.
The 'correct' (IMHO) method is:
-(void) setName: (NSString *) theName
{
if (theName == name) return; // if they're equal, no need to do anything further
[name release];
name = [theName copy]; // sets name to nil if theName is nil
}
For most objects you'll actually want to -retain instead of -copy on that third line, but for strings it's almost always better to copy.
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
See https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa for some reasons as why you should use auto-release vs release.
In the Incorrect code above, the next time name is referenced after setName is called, you'll get an exception error, since the object will have been released. You can use either the "Correct" code, or wrap your stringWithString call in an explicit retain call:
name = [[NSString stringWithString: theName] retain];
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
A string created with stringWithString: isn't owned by the NSString, it is owned by the NSAutoreleasePool (although multiple places can retain an object, making ownership shared).
With stringWithString:, the string will become invalid when the autorelease pool is next processed (normally during the application's next event loop) because the NSAutoreleasePool will release its pointer. If you have not retained the string before then, any pointer you have to it (name in the case of your class) will be invalid (the variable name will still exist but it will point to garbage).
Autorelease is good, if you don't intend to keep any pointers to the NSString but since you do intend to keep a pointer, you'll need to retain the NSString. initWithString: gives you a retain count of 1 automatically.