Why does the reference count when retrieving object from container not increase? - objective-c

I have created a little test project to try to resolve a problem I am having in my main project. I've noticed that when retrieving an object from a container the reference count dosen't increment.
I am confused why this is not the case?
For example this code will not increase the reference count of the hereDoggy object:
//Retrieve the dog, why does this not increment the reference count?
Dog* hereDoggy = [cont1 objectAtIndex:0];
Below is the full example:
-(void)doZombieProblem
{
NSMutableArray* cont1 = [NSMutableArray array];
NSMutableArray* cont2 = [NSMutableArray array];
NSMutableArray* cont3 = nil;
//Create the dog pointer
Dog* doggy = [[Dog alloc] initWithName:#"Bernard"];
//Add to container1
[cont1 addObject:doggy];
//Release the dog pointer
[doggy release];
while ([cont1 count] > 0)
{
//Retrieve the dog, why does this not increment the reference count?
Dog* hereDoggy = [cont1 objectAtIndex:0];
//Add it to cont2
[cont2 addObject:hereDoggy];
//Remove it from cont1.
[cont1 removeObjectAtIndex:0];
//No need to release as we haven't increased the reference count.
//[hereDoggy release];
}
//I should be able to retrieve the dog here from cont2.
Dog* bernard = [cont2 objectAtIndex:0];
//No need to release as we haven't increased the reference count.
//[bernard release];
}

In this case, if you want to increase the retain count for your object you need to send a retain (or a copy) message.
As a rule of thumb
You need always to balance your retains (or copyies) with your releases. If you don't do it you can have memory leaks. Otherwise switch to the ARC feature to avoid the code amount to write and simplify your life.
Here a useful link to understand how Memory Management works.
MemoryMgmt
I commented your code to understand what is going on:
// the object referenced by doggy has a retain count of 1
Dog* doggy = [[Dog alloc] initWithName:#"Bernard"];
// now the retain count is 2 since you added to a container class like NSArray
[cont1 addObject:doggy];
// now the retain count is 1
[doggy release];
Then, within the while statement:
// the retain count still remains 1
Dog* hereDoggy = [cont1 objectAtIndex:0];
// the retain count increases to 2
[cont2 addObject:hereDoggy];
// the retain count goes to 1
[cont1 removeObjectAtIndex:0];
Since, the object is maintained alive by cont2 you are able to access it.
If you do [cont2 removeObjectAtIndex:0]; the retain count reaches 0 and the object is deallocated automatically.

It's your responsibility as the user of the object to manage it's retain count. This is because only you, the consumer, know when you are done with it. That's why just calling [cont1 objectAtIndex:0] doesn't increment it. NSArray has no clue what you have planned with the object it returns.
Think of retain count to indicate the number of things owning something. When it's 0, no one owns it, so let it be garbage collected. If it's 1, then only 1 thing needs it/owns it (and on up).
When you call [cont1 addObject:doggy] NSMutableArray will absolutely increment the retain count on it (behind the scenes), just like when you call [cont1 removeObjectAtIndex:0] NSMutableArray will decrement the retain count on it.
Now, if you need hereDoggy for any period of time, just call retain on it yourself, and then release where appropriate.

Related

dealloc method is not invoked when set an object to nil

I have a question.
I first created an object which extends NSObject, I provided overrides for the description and dealloc methods. Here's my Employee.m file:
#implementation Employee
.....
-(NSString *)description
{
return [NSString stringWithFormat:#"Employ ID: %d has $%d value of assets", [self employeeID], [self valueOfAssets]];
}
-(void)dealloc
{
NSLog(#"deallocating.. %#", self);
[super dealloc];
}
In my main.m, I first created an NSMutableArray to hold a list of Employee objects:
NSMutableArray *employees = [[NSMutableArray alloc] init];
for (int i =0; i< 10; i++)
{
// Create an instance of Employee
Employee *person = [[Employee alloc] init];
// Give the instance varaible interesting values
[person setEmployeeID:i];
[employees addObject: person];
}
and at the end I set employees to nil
employees = nil;
I expected the dealloc method of each Employee object to be called and I would see some logs like:
deallocating.. Employ ID 0 has value.....
deallocating.. Employ ID 2 has value.....
....
However, I didn't see any logs and if I set a breakpoint on the dealloc method, the breakpoint is never hit.
Any thoughts?
A couple of observations:
person = nil does not release an object in non-ARC code. It will in ARC code (at least if it's strong).
In ARC, local objects will be released for you automatically when they fall out of scope. In non-ARC, objects falling out of scope will not be released for you (and if you don't have other references to those objects elsewhere, you'll end up with a leak).
Adding an item to a mutable array will increase the retain count of the item, so even if you include a release in your non-ARC code, the object won't be released until the retain count drops to zero (accomplished by not only releasing the person objects after you add them to the array, but also removing them from the array.
Thus, given that this is non-ARC code, it could be something like:
- (void)testInNonArcCode
{
NSMutableArray *employees = [[NSMutableArray alloc] init]; // employees retain count = +1
for (int i =0; i< 10; i++)
{
//create an instance of Employee
Employee *person = [[Employee alloc] init]; // person retain count = +1
//Give the instance varaible interesting values
[person setEmployeeID:i];
[employees addObject: person]; // person retain count = +2
[person release]; // person retain count = +1 (YOU REALLY WANT TO DO THIS OR ELSE OR NON-ARC PROGRAM WILL LEAK)
// person = nil; // this does nothing, except clears the local var that's limited to the for loop scope ... it does nothing to reduce the retain count or improve memory management in non-ARC code, thus I have commented it out
}
// do whatever you want
[employees removeAllObjects]; // this will remove all of the person objects and they will have their respective retain counts reduced to 0, and therefore the Employee objects will be released
[employees release]; // employees array's own retain count reduced to zero (and will now be dealloced, itself)
}
In ARC code:
- (void)testInArcCode
{
NSMutableArray *employees = [[NSMutableArray alloc] init]; // employees retain count = +1
for (int i =0; i< 10; i++)
{
//create an instance of Employee
Employee *person = [[Employee alloc] init]; // person retain count = +1
//Give the instance varaible interesting values
[person setEmployeeID:i];
[employees addObject: person]; // person retain count = +2
// person = nil; // this would reduce person retain count to +1 (but unnecessary in ARC because when person falls out of scope, it will have it's retain count automatically reduced)
}
// do whatever you want
[employees removeAllObjects]; // this will remove all of the person objects and they will have their respective retain counts reduced to 0, and therefore will be released
// [employees release]; // not permitted in ARC
// employees = nil; // this would effectively release employees, but again, not needed, because when it falls out of scope, it will be released anyway
}
The proper way of freeing objects is to do
[employees release];
Setting it to nil will not release the memory.
By virtue of you being allowed to call [super dealloc], I can assume that you are not using Automatic Reference Counting. This means that you need to explicitly pair every alloc you write with a balancing release call. For you, when you make the array nil, you essentially leaked all of the memory for the employees. You need to loop over the array again to release them all, or better yet since you are learning... Start as soon as possible writing ARC code.
It may be important to note that ARC was created for exactly this kind of situation; it makes sense to our brains, and now it can be a reality if you use the latest tools.

Leaking object by calling addChild

I have a custom class called Hexagon which is a subclass from NSObject. However when I assign it to a sprite and add it to the screen by calling -addChild:, it has a retain count of 2! What should I do, in order to stop that leakage ?
for (int i =0; i < HEXCOUNT; i++){
Hexagon *nHex = [[Hexagon alloc]initWithDicitonary:hexPositions];
CCSprite *theSprite = (CCSprite*)nHex;
NSString *hexName = [NSString stringWithFormat:#"hexagon%d", i];
CGPoint location = CGPointFromString([[EXHEXAGONS objectForKey:hexName]objectForKey:#"position"]);
CGPoint nLocation = ccp(screenSize.width/2 + 68 * location.x,screenSize.height/2 + 39 * location.y);
theSprite.position = nLocation;
[self addChild:theSprite z:1 tag:i];
NSMutableDictionary *hexProperties = [EXHEXAGONS objectForKey:hexName];
[hexProperties setObject:theSprite forKey:#"realSprite"];
[EXHEXAGONS setObject:hexProperties forKey:hexName] ;
[[GameStateSingleton sharedMySingleton]setExistingHexagons:EXHEXAGONS];
[nHex release];
}
Don't rely on retainCount for anything. A retainCount of 2 doesn't mean the object is leaking. Only Instruments can tell you that.
Creating the Hexagon object with alloc/init will add a retain count of +1. Adding it as child will add +1. So depending on where you log the retainCount, it may be correct.
If you worry about memory leaks, by all means start using ARC.
First off, worrying about retain counts is not productive unless you are verifying that it is not getting deallocated when fully released from all the various objects which retain it.
Secondly, presumably you are putting the object into a NSArray, NSSet, or NSDictionary within addChild:z:tag:? So, that would bump it's retain count up by one.
You also cast your Hexagon object to a CCSprite and add it to the NSDictionary hexProperties, which will add another 1 to your retain count.
By the time you release your object at the bottom of the loop, your retain count will be at least 3. After the release it should be at least 2.

Init an object, then store it into an NSArray. Is this going to be a leak?

If an inited object comes to me retained, so I own it, and I store it in an NSArray, which retains that which gets stored in it, can I count on NSArray to see that it's already retained and not increase the count, or do I need to run through the array and decrement the retain count to insure no memory leak?
Sounds like you need to read the Memory Management Programming Guide. Your case is extremely simple. You own the object. You pass it to the array, which now also owns it. You need to release your ownership of it. Otherwise you'll leak it.
To make sure that the ownership of the object which was added into the NSArray is relinquished, send the -release message to the object right after you add it to the NSArray. If you do not do this, then you will indeed have a memory leak.
This is what happens:
NSString *str = [[NSString alloc] initWithFormat:#"%#", #"Blah"]; //retain count is 1, you own this object
[array addObject:str]; //retain count gets bumped to 2
[str release]; //retain count is 1 - relinquishing ownership here.
//There is no leak because when the NSArray is
//deallocated, the object will be sent the release message.
But if you don't send the owned inserted object the -release message, then even when the NSArray is deallocated, the object will only have a retain count of 1 and the memory obtained by the object will never be reclaimed, thereby resulting in a leak.
Whenever you release the NSArray, it'll release everything it retains.
As such, as long as you release the inited object once you've added it to the NSArray (so it's the only thing that retains it) or release it once you've finished with it outside of the array all should be fine.
Incidentally, there's a good blog post called "objective-c memory management for lazy people" that explains such things pretty well and is a handy reference if you're just starting out with such things.
You don't need to do that. NSArray takes ownership of any object that it stores. It will release its objects when it's deallocated. If you retain an object yourself, you take ownership too, and you are responsible for releasing it too.
NSArray will retain your object when you add it, and then release it when you remove it from the array. This is by design. This means that to ensure there's no memory leak, if you already retained the object before adding it to the array, you should release it after removing it from the array:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:10];
NSObject *object = [[NSObject alloc] init]; // retain count of 1 (because of alloc)
[object retain]; // useless, just for example, retain count of 2 (because of retain)
[array addObject:object]; // array is mutable, retain count of 3 (because of addObject:)
[array removeObject:object]; // retain count of 2
[object release]; // retain count of 1
[object release]; // retain count of 0, the object is dealloc'd afterwards
[array release]; // to be sure that we are not leaking an array, too

Objective C Array and Object Release

I have a newbie question regarding when to release the elements of a NSArray. See following pseudo code:
NSMutalbeArray *2DArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0;i<10;i++) {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
for (int j=0;j<5;j++) {
MyObject *obj = [[MyObject alloc] init];
[array addObject:obj];
[obj release];
}
[2DArray addObject:array];
[array release];
}
// use 2DArray to do something
[2DArray release]
My question here is, when I release 2DArray, do I need to explicitly release each of its element (array) first? Also, before I release the "array" object, do I need to release each of its element (MyObject) first?
I am new to Objective C. Please help. thanks.
No, you don't need to tell each object to be released. When you send a release method to an NSArray, it automatically sends a release method to each item inside first.
So in your case, you send [2DArray release]. This automatically sends [array release] to every other array, which sends [obj release] to each object inside each array.
You don't need to release the kept objects. NSArray retains them when you add, and releases them when released. So if you allocate, add to the array, then release, the object in the array will have the retain count of 1. Once the array is freed, the object is released, therefore freed.
When an object is created, it has a retain count of 1. Whenever a object is added to an array, its retain count is increased (in this case to 2). After adding to the array, your code release its hold of the object, dropping its retain count by 1 (to 1 in this case). Then when you release the array, it calls release on everything in it dropping their retain counts by 1 (to 0 in this case). When retain count hits 0 the object is deallocated.
Your code looks correct from a memory management stand point.

What increases an object's retain count?

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