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.
Related
I want to execute a for loop that looks like this:
for (id object in [[MyClass methodReturningSet] allObjects]) {
//do something
}
that methodReturningSet looks something like this:
- (NSSet *)methodReturningSet {
MyObject *object1 = [[MyObject alloc] initWithCustomInfo:info1] autorelease];
MyObject *object2 = [[MyObject alloc] initWithCustomInfo:info2] autorelease];
MyObject *object3 = [[MyObject alloc] initWithCustomInfo:info3] autorelease];
MyObject *object4 = [[MyObject alloc] initWithCustomInfo:info4] autorelease];
return [NSSet setWithObjects: object1, object2, object3, object4, nil];
}
My question, is this safe in terms of memory management?
My current understanding is that 'object' will be sent a release message after the completion of the run loop.
My first question is, does the entire for loop execute within one single run loop?
My second question is, does it matter if the objects get sent release messages via the autorelease pool since the array we're looping over holds a strong reference to all the objects it contains?
hope that was clear . . . any thoughts?
My first question is, does the entire for loop execute within one
single run loop?
Yes.
My second question is, does it matter if the objects get sent release
messages via the autorelease pool since the array we're looping over
holds a strong reference to all the objects it contains?
The NSSet will retain the objects you pass to it. After adding the objects to it, you can release your own references if you don't need them anymore. This can either be normal release calls after adding them to the set (see below), or autorelease calls, as you did it. When you release the NSSet, it will also release (and in this case dealloc) all its objects.
You could use normal release messages to like this:
- (NSSet *)methodReturningSet {
MyObject *object1 = [[MyObject alloc] initWithCustomInfo:info1];
MyObject *object2 = [[MyObject alloc] initWithCustomInfo:info2];
MyObject *object3 = [[MyObject alloc] initWithCustomInfo:info3];
MyObject *object4 = [[MyObject alloc] initWithCustomInfo:info4];
NSSet *set = [NSSet setWithObjects: object1, object2, object3, object4, nil];
[object1 release];
[object2 release];
[object3 release];
[object4 release];
return set;
}
That being said, consider using Automatic Reference Counting (ARC).
My first question is, does the entire for loop execute within one single run loop?
yes.
My second question is, does it matter if the objects get sent release messages via the autorelease pool since the array we're looping over holds a strong reference to all the objects it contains?
Using autorelease or releasing the objects explicitly will not make any difference, since as you say they are also retained by the NSSet object; so the will live for the whole duration of the for loop and get released when the autoreleased NSSet returned through methodReturningSet is deallocated.
You might possibly sightly improve things if you do not autorelease that NSSet and release it explicitly just outside of the loop (but this will only make a difference if you do not immediately return after leaving the loop).
When I compile with the analyzer, I get a couple of messages. I have these properties declared:
#property (nonatomic, retain) SyncServicePrimary *syncAndCartOne;
#property (nonatomic, retain) SyncServiceSecondary *syncAndCartTwo;
This method is called from applicationDidBecomeActive and I get "Potential leak of an object allocated".
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
self.syncAndCartOne = [[SyncServicePrimary alloc] init];
self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
This is called in applicationWillResignActive; here I get "Incorrect decrement of the reference count of an object".
-(void) removeTheCartObjects{
[self.syncAndCartOne release];
self.syncAndCartOne = Nil;
[self.syncAndCartTwo release];
self.syncAndCartTwo = Nil;
}
If I set the objects to autorelease, the error goes away, but I want the objects to be released when the app hides itself.
Is this something I am doing right but that is split too far for the analyzer to see the start and end, or is this something I can do better/properly so it won't complain?
Its more than likely that I am missing a simple concept with regard to release and alloc cycles (I've come from PHP and C#).
Your problem is here:
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
self.syncAndCartOne = [[SyncServicePrimary alloc] init];
self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
You're creating the objects and then retaining them (because of the property declaration), so they have a reference count of 2, when only one object is referencing them.
You should do it like this:
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
SyncServicePrimary *primary = [[SyncServicePrimary alloc] init];
self.syncAndCartOne = primary;
[primary release];
SyncServiceSecondary *secondary = [[SyncServiceSecondary alloc] init];
self.syncAndCartTwo = secondary;
[secondary release];
}
You have defined the properties with attribute retain, so the analyzer assumes that the setter method for the property looks like this:
- (void)setSyncAndCartOne:(SyncServicePrimary *)newValue
{
[newValue retain];
[self->_syncAndCartOne release]; // access the instance variable holding the property value
self->_syncAndCartOne = newValue;
}
If you use #synthesize, the setter method will look like that.
So, when makeTheCartObjectsForCountry: returns, the object in syncAndCartOne has a retain count of 2, but should only have a retain count of 1. That's why using autorelease fixes it.
You shouldn't be doing [self.syncAndCartOne release] for the same reason. The setter method will send the old object a release when you assign nil to the property.
I have a memory leak problem that just can not understand! Watch this initialization method:
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}
return self;}
Then this little variation (note self.tipologia):
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:#"Test"];
}
return self;}
In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:
[NSDictionary dictionaryWithContentsOfFile:pathOpere]
This is the dealloc method:
- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}
Remember that alloc returns an object that you own.
If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.
If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.
Either way, that is your leak.
The property should be declared as copy; if it already is, don't try to fix the leak by changing that.
You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.
So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.
You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.
When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.
The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.
Try
nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
or
self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];
instead of self.xxx = [[yyy alloc] init...].
In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the #property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.
BTW, there's no point calling +dictionaryWithDictionary:. Just use
NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera];
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
What is the advantage of doing this:
NSArray *array = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
self.hintArray = array;
[array release];
Instead of assigning directly to my class variable like this:
self.hintArray = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
Why do we create a temporary local object then release it instead of just assigning to our class variable?
Others have already pointed out the memory issues, but here is the best way to do it in a single step:
self.hintArray = [NSArray arrayWithObjects:#"Year", "#Capital", ..., nil];
The convenience class method +arrayWithObjects returns an array that has already been autoreleased, so you simply don't need to worry about it any more. Your property accessor will take care of copying or retaining it. (assuming, of course, that your hintArray property is set up as a retain or copy property).
You could, but you have to remember to release it once before moving on. The assignment to self.hintArray (assuming it is a synthesized setter that retains on set) will bump the retainCount:
NSArray *array = [[NSArray alloc] initWithObjects:...]; // retainCount is 1
self.hintArray = array; // retainCount is 2
[array release]; // retainCount is 1
and:
self.hintArray = [[NSArray alloc] initWithObjects:...]; // retainCount is 2:
// one for the alloc
// and one for the assign
[self.hintArray release]; // retainCount is 1
Because in the Objective-C reference counted memory management scheme the creation of the array will increment the reference count and if you do not store the return value in a variable you can send a release message to you will have no way to decrement that count and will introduce a memory leak.