iOS: Object release in ARC environment - objective-c

Could anyone please tell me am I handling memory correctly in following code in ARC environment? My concern is how would dict object released if I can't use release/autorelease in ARC! I know if it is strong type then it's get released before creating new one but in following look I don't know how would it works.
NSMutableArray *questions = [[NSMutableArray alloc] init];
for (NSDictionary *q in [delegate questions])
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:[q objectForKey:#"text"] forKey:#"text"];
[dict setValue:nil forKey:#"value"];
[dict setValue:[NSString stringWithFormat:#"%d",tag] forKey:#"tag"];
[questions addObject:dict];
dict = nil;
}

Yes, you are handling your dict correctly.
If you have a snippet like the following:
{
id __strong foo = [[NSObject alloc] init];
}
When you leave the scope of the variable obj, the owning reference will be release. The object is released automatically. But it's not magic stuff involved. ARC will put (under the hood) a call like the following:
{
id __strong foo = [[NSObject alloc] init]; //__strong is the default
objc_release(foo);
}
objc_release(...) is a sort of release call but since it bypasess objc messaging it's very performing.
Furthermore, you don't need to set the variable dict to nil. ARC will handle this for you. Setting an object to nil cause a reference to an object to disappears. When an object has no strong references to it, the object is released (no magic involved, the compiler will put the right calls to make it happens). To understand this concept suppose you two objects:
{
id __strong foo1 = [[NSObject alloc] init];
id __strong foo2 = nil;
foo2 = foo1; // foo1 and foo2 have strong reference to that object
foo1 = nil; // a strong reference to that object disappears
foo2 = nil; // a strong reference to that object disappears
// the object is released since no one has a reference to it
}
To have an understanding of how ARC works I really suggest to read Mike Ash blog.
Hope that helps.

Related

Can I release ivar in an instance method of the same class? (Objective-C)

Can I release ivar in an instance method of the same class? Because I want to alloc-init it again. Does this conflict with the other [_myNSStringIvar release]; in -(void)dealloc?
// in the middle of an instance method
if(_myNSStringIvar != nil){
[_myNSStringIvar release];
}
_myNSStringIvar = [[NSString alloc] initWithString:(NSString *)[aDictionary objectForKey:#"key"]];
You can release whenever you don't need the referenced object anymore. You don't even have to check for nil before releasing, as you can just send release to nil.
[_myNSStringIvar release];
_myNSStringIvar = [[NSString alloc] initWithString:(NSString *)[aDictionary objectForKey:#"key"]];
You also don't have to alloc] init] a new string object, instead copy the existing object:
[_myNSStringIvar release];
_myNSStringIvar = [aDictionary[#"key"] copy];
That way, you only retain if aDictionary[#"key"] is immutable, and copy if it is a NSMutableString object.
Now if you use a property with the copy attribute, you don't even have to manually release:
#property (copy) NSString *myNSString;
...
self.myNSString = aDictionary[#"key"];
The old object is released in the setter, the new one will be copied.

"Incorrect decrement" and "Potential leak" messages from Analyzer

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.

Why Do I Have to Create An Object and Assign It to A Property in Objective C?

So I had this code, and it did not work:
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
self.resultsArray is nil. But then I changed it to this:
NSMutableArray *myDataArray = [[NSMutableArray alloc] init];
for (NSDictionary *item in data){
[myDataArray addObject:item];
}
self.resultsArray = myDataArray;
[myDataArray release];
and now it worked. self.resultsArray is now populated
So I'm a beginner in Objective C and I was wondering why can I not just directly use it in the property's addObject. Why did I have to create another mutable array, populate it, assign it to the resultsArray property and release the mutable array I made?
Thanks in advance!
EDIT: Also, in a lot of books I've been working on, this is done a lot.
simple answer
You didn't initialize self.resultArray before adding objects to it. It is just a pointer to the value which is nil until you alloc it.
self.resultArray = [[NSMutableArray alloc] init]; before adding objects to it will solve the issue.
However, this way of alloc'ing will create a memory leak, therefore it is not shown in books and examples. Memory leak can happen if the self.resultArray property is marked as retain and by calling alloc it will be retained 2 times.
If self.resultsArray is nil, then [self.resultsArray addObject:item] will NOT add an object to the array, it will just do nothing (because the array will be nil by default, and sending messages to nil is a no-op in Objective-C). When you create a mutable array as a local variable, you can add things to it — then if you assign it to the property, well, everything works as you expect and self.resultsArray will no longer be nil.
Typically when you have properties like this, you'd set them up in your init method:
- (id)init {
// ...
self.resultsArray = [NSMutableArray array];
// or access the ivar directly:
// _resultsArray = [[NSMutableArray alloc] init];
// ...
}
Then as soon as your object is initialized you'll be able to add things to the array. Again, if you don't do this, it will be nil by default, and [self.resultsArray addObject:item] will have no effect.
Chances are you are not initializing the array (I'm going to assume myDataArray is an NSMutableArray).
In your init method, call myDataArray = [NSMutableArray array]; and it'll work
The important thing to note is that you're not creating another mutable array as you didn't have an array to start with. Merely declaring a property or variable does not create an object to go along with it. That's why self.resultsArray starts out as nil.
The working code you have is designed to allow you to explicitly release the array as you are retaining it twice: once when you alloc it and once when you assign it to your property. You only want one of those retains, so you release once.
You could just do:
self.resultsArray = [[NSMutableArray alloc] init];
[self.resultsArray release];
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
This is less code, but it's not as clear. Clarity is important.

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.

Memory leak for NSDictionary loaded by plist file

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