Objective C Memory Management Woes - objective-c

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.

Related

Basic of Objective C

#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.

Returning autoreleased objects using ARC

Assuming that I wrote the code below in a class A:
-(NSArray *) returnListNames {
NSArray *returnList = [NSArray arrayWithArray:myListNames];
return (returnList);
}
And in a class B I get that list in some scope in this way:
{
/* Without ARC I would retain the array returned from ClassA
to guarantee its reference like this:
[[myClassA returnListNames] retain]; */
NSArray *myNames = [myClassA returnListNames];
}
Considering that the returnList was allocated using an autorelease method, how can I guarantee that I won't lose the reference to it using ARC (under which I can't use retain)?
Will I have to use [[NSArray alloc] init] on the myNames array? Or I must use alloc on returnList instead of an autorelease method? Or can I just rely on ARC? Or is there another solution?
ARC will handle this for you, so you can just rely on it and go about your business with that array. If it sees that you need to retain myNames, it will add a retain call for you for example, or do whatever else it actually does when you compile the code that uses it.

objective-C: simple question about copy/retain NSString

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).

Populating Object in Objective-C

I'm trying to populate a dictionary dynamically in a for loop like so:
pseudo-code
myObject = new Object
myDict = new Dictionary
for(int i; i < 10;i++)
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic = value:myObject key:i
end for
So my question is in Objective-C, can I just keep assigning new data to the myObject without releasing it every time in the loop? I'm still trying to figure out memory management in Objective-C.
That depends on what myObject actually is and how you have its properties defined. Assuming you have them defined to release such as here:
#property (nonatomic, retain) SomeClass *myProperty;
then yes, you can do that. The setters synthesized automatically for you by objective-c release and set to nil old values of properties before retaining and assigning new values.
However, although there is no problem with your memory management, there is still a problem with your code. Remember that myObject is a pointer, just like in C or C++. That means that if you add it to myDic and then modify the properties of the object later (such as on the next iteration of your for loop), those changes will be reflected when you pull the object out of the dictionary at some point in the future. What you want is something like this:
myObject = nil
myDict = new Dictionary
for(int i; i < 10;i++)
myObject = new Object
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic setValue:myObject forKey:i
myObject release
end for
What this does is release myObject after putting it in the dictionary (all cocoa collection classes retain any object you put into them and release the objects when they are either removed or the collection itself is deallocated) so you don't have a leak, as well as allocate a new instance of Object at every iteration so you aren't modifying the same object over and over again.
If you haven't been reading Apple's Memory Management Guide for Cocoa, I'd highly recommend doing so. It's very informative.
Memory management in Objective-C is done through reference counting. When you allocate an object, it has a reference count of 1. The 'retain' method increases the reference count while 'release' decreases it. When the reference count reaches 0, the 'dealloc' method is called (you should never call 'dealloc' explicitely) and the object is freed.
You can also call 'autorelease' on an object, which will decrease the reference count "some time later". This allow you to make use of the object without worrying about releasing it.
For your question. When you add an object to a container, a 'retain' call is made on the inserted object. This means that you have to 'release' the inserted object:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
for( int i = 0; i < 10; ++i ) {
Object *myObject = [[Object alloc] init];
[myDict setObject:myObject forKey:[NSNumber numberWithInt:i]];
[myObject release];
}
You could also use:
Object *myObject = [[[Object alloc] init] autorelease];
and you wouldn't have to call 'release' after the insertion.
Your question is very open, it depends on how your Object and Dictionary is implemented.
First, you allocate object only once and release it in loop 10 times -> memory problem.
If we get past that. Assume that you allocate new object in every iteration of loop.
Take NSMutableDictionary and MyObject that extends NSObject as an examples of your Object and Dictionary.
When you call setObject:forKey on NSMutableDictionary instance, the object will receive retain call so dictionary keeps reference of it.
When you release it at the end of iteration the dictionary still keeps reference to it so it is ok.
Another thing to keep in mind if you use this in a big loop:
Object *myObject = [[[Object alloc] init] autorelease];
is the fact that autoreleased objects go to autorelease pool. Pool gets cleaned at the end of current event processing. If you create lots of objects it can take a lot of time to get through it at the end of event processing. In that case you might chose to create your own autorelease pool only for the loop - but I guess that's more advanced topic.
Definitely have a look at some Objective-c and memory management references from Apple.
What you're doing works with only one object and puts that one object into the dictionary ten times. It does not put ten separate objects into the dictionary.

Object ownership in stringWithString and initWithString in NSString

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.