Here is code I am referring to.
// Person.h
@interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
@end
// Person.m
@implementation Person
- (id)init {
if (![super init]) return nil;
firstName = @"John";
lastName = @"Doe";
}
@end
// MyClass.m
@implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
@end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You'd use alloc, retain and release to indicate which objects needed to be retained or released. The retain method would increase an object's retain count, and release would decrease it. Just as with ARC, a retain count of zero meant the object was removed from memory. These days, ARC automates this process.
The system that Objective-C uses is called retain/release. The basic premise behind the system is that if you want to hold on to a reference to another object, you need to issue a retain on that object. When you no longer have a use for it, you release it. Similar to Java, each object has a retain count.
You send an object a retain message when you want to prevent it from being deallocated until you have finished using it. An object is deallocated automatically when its reference count reaches 0 . retain messages increment the reference count, and release messages decrement it.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit: Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like @property (retain) suchAndSuchIvar, and use @synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = @"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: @"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init]
, you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With