How to release objects stored in an array? - objective-c

Please look at the code below and suggest the best approach. I can't quite tell whether the code is correct. When adding objects to arrays, do they get a retain count? In the second function, am I releasing the local variable "mySubview" or the original object?
// this is a class property
myArray = [[NSMutableArray alloc] init];
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
[mySubview release];
}

When adding objects to arrays, do they
get a retain count?
Yes.
In the second function, am I releasing
the local variable "mySubview" or the
original object?
UIView *mySubview;' defines a local variable, mySubview, which is a pointer to -- a reference to -- an instance of the UIView class. There is no such thing as a "local object" or "stack object" in Objective-C (save for blocks, but that is beyond the scope of this question).
So, no, when you call [mySubview release] you are sending -release to the instance of of UIView included in notification.
That release is balancing the retain implied by the alloc. Which isn't the right pattern at all. You should do something like:
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
[mySubview release];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
}
Oh, by "class property", I'm assuming you mean "instance variable"?

Related

Regarding memory management in Objective C

According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!

NSDictionary class changing if held in external Singleton?

I am watching a somewhat cruel behaviour momentarily: I have a ViewController for building a View programmatically. For this purpose I have stored the names of the UILabels that will be displayed in a NSDictionary that is held in an external class which is a singleton.
Unfortunately the NSDictionary is not accessible if I want to use the values in loadView. So I made some tests: The NSDictionary and its contents are availbale in init and the class is, of course, NSCFDictionary. If I have a look at it in loadView the class sometimes is NSCFDictionary and sometimes also CALayer or NSString?! I absolutely don't know what is happening??? This is the code I use:
- (id) init
{
self = [super initWithNibName:nil bundle:nil];
if (self)
{
UITabBarItem *tbi = [self tabBarItem];
[tbi setTitle:#"xxx"];
}
NSEnumerator *num = [[[ValueDispatcher dispatcher] labelDic] keyEnumerator];
NSLog(#"Class(init): %#", [[[ValueDispatcher dispatcher] labelDic] class]);
NSLog(#"No: %i", [[[ValueDispatcher dispatcher] labelDic] count]);
for (id key in num)
{
NSLog(#"Key %# Value %#", key, [[[ValueDispatcher dispatcher] labelDic] valueForKey:key]);
}
return self;
}
- (void)loadView
{
NSLog(#"Class(loadview)1: %#", [[[ValueDispatcher dispatcher] labelDic] class]);
NSLog(#"No: %i", [[[ValueDispatcher dispatcher] labelDic] count]);
NSEnumerator *num = [[[ValueDispatcher dispatcher] labelDic] keyEnumerator];
for (id key in num)
{
NSLog(#"Key34 %# Value %#", key, [[[ValueDispatcher dispatcher] labelDic] valueForKey:key]);
}
...
At which point between init and loadView can or will a NSDictionary be changed?
Btw, another info that might be important: If I use the above code and the NSDictionary is filled by an external service everything works fine. But if I fill the NSDictionary from a stored plist during startup it fails and I watch the described behaviour...
If I have a look at it in loadView the class sometimes is NSCFDictionary and sometimes also CALayer or NSString?
this (typically) means you have a reference count issue or you have not unregistered your observers correctly -- assuming you never set the dictionary. run instruments with zombies enabled.
At which point between init and loadView can or will a NSDictionary be changed?
a lot can happen in that time beyond the code you posted.
You'll need to retain that dictionary for as long your singleton needs it.
If you're using ARC, just make sure that ivar and/ or property are strong.
If you aren't using ARC, and you have a property setter to manage this for you, make sure you are actually using that setter.
And if no ARC, and you're setting your ivar directly, just make sure to retain the dictionary (and release the old one, if any)

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

Objective-C Properties and Memory Management

Given the following property definition:
#property (nonatomic,retain) MyObject* foo;
does the following code cause a memory leak:
self.foo = [[MyObject alloc] init];
?
It looks like the alloc call increments the retain count on the object to 1, then the retain inside the property setter increases it to 1. But since the initial count is never decremented to 0, the object will stick around even when self is released. Is that analysis correct?
If so, it looks like I have two alternatives:
self.foo = [[[MyObject alloc] init] autorelease];
which is not recommended on the iPhone for performance reasons, or:
MyObject* x = [[MyObject alloc] init];
self.foo = x
[x release];
which is a bit cumbersome. Are there other alternatives?
Are there any alternatives?
No.
You are not going to be able write much of an iPhone application without using autorelease and the Cocoa Touch library uses them in many places. Understand what it's doing (adding the pointer to a list for removal on the next frame) and avoid using it in tight loops.
You can use class method on MyObject that does alloc/init/autorelease for you to clean it up.
+ (MyObject *)object {
return [[[MyObject alloc] init] autorelease];
}
self.foo = [MyObject object];
The easiest way to manage a retained property on the iPhone is the following (autorelease is not as bad as you think, at least for most uses):
-(id)init {
if (self = [super init]) {
self.someObject = [[[Object alloc] init] autorelease];
}
return self;
}
-(void)dealloc {
[someObject release];
[super dealloc];
}
The autorelease releases the reference to the floating instance which is assigned to self.object which retains its own reference, leaving you with the one reference you need (someObject). Then when the class is destroyed the only remaining reference is released, destroying the object.
As described in another answer, you can also create one or more "constructor" messages to create and autorelease the objects with optional parameters.
+(Object)object;
+(Object)objectWithCount:(int)count;
+(Object)objectFromFile:(NSString *)path;
One could define these as:
// No need to release o if fails because its already autoreleased
+(Object)objectFromFile:(NSString *)path {
Object *o = [[[Object alloc] init] autorelease];
if (![o loadFromFile:path]) {
return nil;
}
return o;
}
You are right, self.foo = [[MyObject alloc] init]; is leaking memory. Both alternatives are correct and can be used. Regarding the autorelease in such a statement: keep in mind that the object will released by the autorelease pool as soon as the current run loop ends, but it will most probably be retained a lot longer by self, so there is no issue with memory usage spikes here.

Why is Instruments reporting this as a memory leak?

I have some code that looks like the following:
NSMutableArray *bar = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableDictionary *foo = [[NSMutableDictionary alloc] initWithCapacity:0];
[foo setObject:[NSNull null] forKey:#"yay"];
[bar addObject:foo];
[foo release];
Instruments is showing that foo is leaking. I understand why that is happening. Foo's retain count when alloc'd is 1. Then when bar addObject's foo, the retain count goes to 2. Later when I release foo, it goes down back to 1. Still a leak. However, later on in my code, (in a separate method, which is why I think this might be shown as a leak)
[bar removeAllObjects];
Why is foo shown as leaking if I do removeAllObjects later on?
** NOTE **
I didn't include it in my original post, but bar is indeed being released in the classes dealloc method.
I think (and I think you hinted at this possibility as well) that Instruments is marking it as a potential leak, because it hasn't looked ahead far enough to see that bar will be responsible for removing/releasing all its objects in said separate method..
Given what you show, it is bar that never gets released. Calling [bar removeAllObjects] only removes the objects it contains. Instead, you should call [bar release] when you are done with bar. This will automatically release all of the objects that bar holds, plus release the bar object itself.
You state that you understand the memory management concepts, so perhaps you just didn't show bar being released in your example.
edit: I think craig has the right idea in his answer. One way to avoid the warning (maybe) would be to allocate bar in the class init method. I usually find it beneficial to maintain a symmetry between my init and dealloc methods when it comes to member variables, and this would be a good example:
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
bar = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[bar release];
[super dealloc];
}
- (void)YourMethod
{
NSMutableDictionary *foo = [[NSMutableDictionary alloc] initWithCapacity:0];
[foo setObject:[NSNull null] forKey:#"yay"];
[bar addObject:foo];
[foo release];
}
The NSMutableArray should be releasing it when removeAllObjects is called. You shouldn't need to release or add it to an autorelease pool.
From O'Reilly's chapeter on Memory Management:
When you add an object to a collection, it is retained. When you remove an object from a collection, it is released. Releasing a collection object (such as an NSArray) releases all objects stored in it as well.
Perhaps something else is going on in instruments?