What increases an object's retain count? - objective-c

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

Related

Does receiver of initWith... methods take ownership - memory mgmt

toNewbie question: Could someone help me understand if the receiver of an initWith.... method take ownership of the returned object and should manage it.
For example:
NSString *msg; //could be used to hold some data, then
[msg initWithFormat: #"some text"]; //do I need to do a retain or is the method returning an auto-released string object
Just trying to get my head wrapped around methods that don't use alloc explicitly
Methods with init in the name are generally called in conjunction with alloc, as in:
MyObject *obj = [[MyObject alloc] init];
alloc returns an object with a +1 retain count, so you shouldn't need to retain it.

Objective-C Property assignment without #property

I'm currently developing an iOS application which was started by another developer.
Usually, I make a property for every instance variable (assign for int, bool etc. / retain for all classes).
So in my projects, this line causes a leak:
myVar = [[NSString alloc] init]; (alloc/init +1, retain in setter +1, release in dealloc -1 => +1)
So I use:
NSString *tmpMyVar = [[NSString alloc] init];
[self setMyVar: tmpMyVar];
[tmpMyVar release];
Or:
NSString *tmpMyVar = [[[NSString alloc] init] autorelease];
[self setMyVar: tmpMyVar];
In this new project, the previous developer didn't use #property/#synthesize so I'm wondering what will be the result of the previous line of code in this context (it doesn't call setter I guess)? Memory Leak?
The previous developer releases variable in dealloc method, just like me.
Thank you very much!
Since it directly assigns the instance variable to the allocated object it's retain count is 1 (because, like you said, a setter isn't called).
And because it's released in dealloc, it's all balanced out. So no memory leaks.
So in my projects, this line causes a leak:
myVar = [[NSString alloc] init]; (alloc/init +1, retain in setter +1, release in dealloc -1 => +1)
No,it wouldn't even in your projects, because, as you pointed out, no setter is used.
Also, when using properties, it is the recommended way to access instance variables directly in the init method, instead of using setters.
To inspect for questionable memory-leaks like your example, also use the clang static analyzer or instrument's leak tool.
You need to look at the other developer's setter implementation. Make sure they release the existing value and retain the new value; something like:
- (void)setMyString:(NSString *)string
{
[string retain];
[_string release]; // ivar
_string = string;
}
The only advantage to implementing your own setter/getter methods is to do something (other than setting the ivar) when a value is set. If the methods don't do anything like this then why not change all implementations to #property/#synthensize?

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 setter method memory management

I have question about allocating and releasing objects:
if I have code like this:
MyObject *object = [[MyObject alloc] init];
NSMutableString *string = [[NSMutableString alloc]initWithString:#"bla"];
object.myString = string;
NSLog(#"retain count: %d",[object.myString retainCount]); //gives me 2
[string release];
NSLog(#"retain count: %d",[object.myString retainCount]); //gives me 1
Than I have exactly what I want. I need just one reference and I have retain count 1
but
if I use
object.myString = [[NSMutableString alloc]initWithString:#"bla"];
my property look like this:
#property (nonatomic,retain) NSMutableString *myString;
one alloc, and one setter method with retain gives me as retain count 2
If I release the object after resignment than the app crashes. I dont know why?
So , do i have to always create an object with a temporary reference, than assign to real reference and release the temp reference like first code?
or is there any other way?
Yes and no. Generally, this is a common pattern:
// create the object, retain count 1
MyObject *myObject = [[MyObject alloc] init];
// increment the retain count in the setter
self.myObjectProperty = myObject;
// let go of the object before the end of the current method
[myObject release];
You can avoid the release, sort of, by using autorelease pools. More accurately, you indicate that you want the object to be released soon:
MyObject *myObject = [[[MyObject alloc] init] autorelease];
self.myObjectProperty = myObject;
// all done!
With many of the Apple-provided classes, you can use class methods other than alloc/init to get objects that are already autoreleased. Your example could be rewritten as:
MyObject *myObject = [[MyObject alloc] init];
myObject.myString = [NSMutableString stringWithFormat:#"bla"];
A final note: -retainCount is a blunt object. Particularly with NSStrings and other built-in classes, it may return results that are quite different from what you expect. Generally you should avoid it.

simple NSMutable array question

umm So simple question here:
I have an instance of NSMutableArray declared in my header
NSMutableArray *day19;
#property (nonatomic, retain) NSMutableArray *day19
implementation:
#synthesize day19;
In my viewDidLoad
self.day19 = [[NSMutableArray alloc] init];
In the myMethod where I want to add objects to the array I:
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
However... when i check the day19 array there is nothing in it. If I conversely add the newObject to a tempArray within the myMethod scope and then set the day19 array to the tempArray, day19 has the objects.
Super basic I know just must be a confused morning or something...
thanks for any help
Is day19 actually an instance variable? In the snippet, it's not clear when it's declared as an instance variable or just as a variable outside the scope of the class.
A couple of things:
Are you sure viewDidLoad is the right place to init your array? Confer here.
Also, at least from the code you've got posted, it looks like you're being sloppy with your retains. If your property is a retain type, you should not be writing:
self.myProperty = [[Something alloc] init]; // double retain here, bad
You should instead be writing something like:
self.myProperty = [[[Something alloc] init] autorelease]; // single, good
Also, with
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
unless you have a
[newObject release];
down the pike, you've got a memory leak.
In my viewDidLoad
self.day19 = [[NSMutableArray alloc] init];
In the myMethod where I want to add objects to the array I:
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
However... when i check the day19 array there is nothing in it. If I conversely add the newObject to a tempArray within the myMethod scope and then set the day19 array to the tempArray, day19 has the objects.
Let me guess: You checked the array with code like this:
NSLog(#"day19 contains %lu objects", [day19 count]);
Remember that a message to nil does nothing and returns nil, 0, or 0.0. That's why the output said 0 objects: You don't have an array in the first place. The most probable reason for that is that viewDidLoad hasn't been called yet, so you have not yet created the mutable array.
It's also possible that you have an array (i.e., the view has been loaded) at the time you examine the array, but you didn't have an array yet (the view hadn't been loaded yet) at the time you tried to add to the array, so your addObject: message fell on deaf ears.
Consider creating the array earlier. You probably should be creating it in init or initWithCoder:.
A third possibility is that you examined the array before you ever added to it. Make sure you log or break at both points, so you know which one happened first.
Whatever the problem is, you also need to either assign the array to the instance variable, not the property, or autorelease the array before assigning it to the property. Otherwise, you're over-retaining the array, which means you will probably leak it later on. You probably need to review the Memory Management Programming Guide for Cocoa.