Objective-C memory management - pretty sure I'm doing this all wrong - iphone

After 3 hours or so, I've finally managed to fix a memory leak in a view controller. The leak was caused by a UIPickerView that has its property set to 'retain' in the header file.
The following code managed to fix it:
- (void)viewDidLoad {
[super viewDidLoad];
myPicker = [[[UIPickerView alloc] initWithFrame:CGRectZero]autorelease];
}
- (void)dealloc {
[super dealloc];
[myPicker release];
myPicker = nil;
}
Please don't tell me how shocking this code is... I know it's bad. I've got a release, and an autorelease. Problem is, if I change or remove any part of the above, the memory leak returns.
I though I knew how objective C memory management works, obviously not...
Why does the above code fix the memory leak, and what might a correct version of the code look like?
-
EDIT:
If anyone has the same problem, or is interested - the problem was that one of the other objects in my class was set to 'retain' rather than 'assign'. (If you don't own an object, it should have the property assign, not retain).
Like Cannondale said, removing the extra retain fixes everything, and only one release is necessary.

You must be doing a retain on myPicker somewhere else in your code. Your myPicker allocation line will release that memory as soon as the stack unrolls for the viewDidLoad call (that is what the autorelease is telling it do do).
You must be doing a retain somewhere after that point, if not then your [myPicker release] will be trying to free unallocated memory with unpredictable results.
What you should be doing is allocating the memory in viewDidLoad (so remove the autorelease). Make sure you don't retain the object anywhere else and release myPicker from the dealloc.
Also ... what bbum said re the dealloc ;)

What Cannonade said. This should work:
myPicker = [[UIPickerView alloc] initWithFrame:CGRectZero];
Your dealloc is busted, too. Call to super always has to be last (think about it) and that can lead to undefined behavior.
- (void)dealloc {
[myPicker release];
myPicker = nil;
[super dealloc];
}

Related

confused by ARC

The codes below I used on Xcode 3.2 and worked very well
#interface MoObject : UIViewController
{
NSMutableArray *categoryArray;
}
#property (nonatomic, retain) NSMutableArray *categoryArray;
#end;
#implementation MyObject
#synthesize categoryArray;
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *a = [[NSMutableArray alloc] init];
categoryArray = a;
[a release]; //AAA:I can not release this, it does not report compiling error , but after some operation, it will cause exec bad error
}
- (void)dealloc {
[category release];
[super dealloc];
}
I just move to Xcode 4.3.1 iOS 5.1
the same function causes so many exec bad error.
Even I close the ARC of whole project.
The problem still exist, it looks like I can not release the object array at point AAA
Welcome any comment
The point of ARC is that it handles the retain/release code for you. Since your array "a" is declared locally, when that function ends, ARC will insert the code release it for you. Same for dealloc - you don't need it with ARC (and actually can't use it).
But if you don't like or want to learn ARC you don't have to use it. It's an option when you create a new project.
You are not using ARC. If you were, code using -release would not even compile. Neither would calling [super dealloc] in your -dealloc method.
What you have is just an ordinary memory management bug. You are assigning a directly to the instance variable categoryArray. You are not using the setter which is synthesized for the property and it is the setter which does the proper memory management. Therefor, categoryArray ends up pointing to a deallocated object. If you were to use either self.categoryArray = a or [self setCategoryArray:a], which are equivalent statements, then your bug would go away.
Under ARC, this bug would be mostly hidden. The categoryArray instance variable would by default be __strong, so the array would be retained at least as long as categoryArray pointed to it and you wouldn't get EXC_BAD_ACCESS errors. However, it's still buggy to directly assign to it, bypassing the setter, except within initializer methods.

In dealloc method set any delegate to nil is needed or not needed

I have created tableview in my view by programmatic like below
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 44, 320, 370) style:UITableViewCellStyleDefault];
table.delegate = self;
table.dataSource = self;
table.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.view addSubview:table];
in dealloc method i write like below
table.delegate = nil;
table.dataSource = nil;
[table release];
table=nil;
this the better way or below one is better
[table release];
table=nil;
I want to know if i dont reset delegate and dataSource what will happen
Thanq
If you are deallocating an object that acts as the delegate to other objects, you need to make sure that you have set their delegates to nil, before you call [super dealloc] (assuming the normal pattern that objects do not retain their delegates). This is because when [super dealloc] has returned, this object is no longer a valid object and the objects it is a delegate to effectively have dangling references, if they have not been set to nil.
In this particular case, you would probably get away without doing it because your object's dealloc probably won't get called except when the UI is being dismantled and the table view no longer needs to use its delegate or data source, but don't bet on it.
From Setting delegate to nil in dealloc:
It's a defensive programming move. It's clearing out the reference to the delegate object incase something else in your object tries to access the delegate after you've told it that you're done with it. As part of your dealloc you might have a method or do something that triggers a KVO notification that makes a call to the delegate. So setting the delegate's reference to nil prevents that from happening. If it did happen you could end up with some oddball crashes that are fun to reproduce and fix.
To add to the answers above, you do not need
table = nil;
in your dealloc. It won't hurt, but it is not necessary to nil out your ivars. Your view is being dealloc'ed and therefore your ivars will no longer be accessible. You are probably confusing that with:
self.table = nil;
which can function as an alternative way to release if you are accessing the ivar via a property.
Of course if you have ARC turned on, then you don't need the release at all.
And to answer your actual question, if you don't nil out the table's delegate and datasource on the dealloc of the view....nothing will happen. They are set to the view, which is in the process of being released. In this case, you will have no issues not doing it. In theory it's good form.

Best practices, fire-and-forget asynchronous classes?

I have a class whose sole purpose is to go download a specific file off the net, store it locally and then return the local path of the stored file.
I use this class based on whether I have a local copy of the file in question or not, and I sometimes call it up multiple times at the same time, if more than one file needs downloading. The way I use it is simply
Loader *l = [[Loader alloc] initWithDelegate:self];
[l downloadFile:someFile];
[l release];
The thing is, in order to keep it around until it's done downloading, I am actually doing [self retain]; in the class, then [self autorelease]; when it's done. This feels hacky though. How do people deal with this?
I agree that [self release] and [self autorelease] feel weird, but that doesn't mean they're wrong. In some situations, they can be the right thing to use (and I've used them before).
If you want to avoid them, however, you might consider making a LoaderManager class that simply owns the Loader objects until they're done loading stuff. It'd essentially be a wrapper around an array, and you'd use it like this (or something):
#interface LoaderManager : NSObject {
NSMutableSet *loaders;
}
#end
#implementation LoaderManager
- (id)init {
self = [super init];
if (self) {
loaders = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc {
[loaders release];
[super dealloc];
}
- (void)addLoader:(Loader *)loader {
[loaders addObject:loader];
}
#end
And then your Loader object would do:
[myLoader downloadFile:someFile manager:aLoaderManager];
Which internally would simply invoke:
[aLoaderManager addLoader:self];
Under the circumstances, I think it's fine for your Loader to retain and autorelease itself. The most expedient solution might be to just add a detailed comment to your code that explains why it does what it does. The biggest problem is that Loader takes a delegate. Delegators don't usually retain their delegates in order to avoid retain cycles, but in this case it seems possible that the delegate could be deallocated before the Loader is finished downloading its file. If that happens, a crash is likely. So, if you want to continue with this fire-and-forget style, you might want to have Loader retain its delegate.

memory leak when adding objects to nsarray

In the code below, PersonListArray is an NSMutableArray and I'm getting the list of persons from the sqlite DB and adding it to my array.
Person* tmpPerson = [[Person alloc] init];
tmpPerson.personName = #"Mike";
tmpPerson.personEmail = #"mike#mike.com";
[PersonListArray addObject:tmpPerson];
[tmpPerson release];
Even though I'm releasing the Person object here, its giving a memory leak which I'm guessing is due to the array holding a reference count to it. I'm using the array elsewhere in the program and then releasing it for sure.
What's the best practice to create new objects for an array and not run into this issue?
In the dealloc method where i release the array
-(void) dealloc{
[PersonListArray release]; // this contains the numerous Person objects
[super dealloc];
}
should i manually release them like this instead ?
-(void) dealloc{
for (int i = 0; i<PersonListArray.count;i++)
{
Person * tmpPerson = [PersonListArray objectAtIndex:i];
[tmpPerson release];
}
[PersonListArray release];
[super dealloc];
}
The code you are showing us is correct and contains no leaks. The last section is wrong, though, and would case your program to crash because you are releasing Person objects you no longer own.
Your code, as initially implemented, is correct. An array retains onjects added to it and releases them either when they're removed from the array or when the array is dealloced. No need to go through the array yourself.
What means are you using to detect the leak? If it's Instruments then you may be misunderstanding what it is telling you. When it detects a leak, it can show you where the memory was first allocated. It can't show you which object is responsible for the leak. I would therefore guess the given dealloc method is never called (because that object is leaked) or that someone else retains the array and doesn't release it. Try putting an NSLog in dealloc to ensure that it is occurring; as a run once test you could try logging PersonListArray after releasing it — if that doesn't cause a memory exception then almost certainly someone else has retained it.
[REMOVED: my original text "Try adding an NSLog of [PersonListArray retainCount] to your dealloc to figure out which is the case."; see comment from bbum below]
The most common cause of accidental additional retains is #property/#sythesize properties that are set to retain but for which a matching release is not added to dealloc.
Somewhere else in your app, you probably call [PersonListArray objectAtIndex:n] and pass it around to various other parts of your app. One of the other parts of your app is probably leaking it.
If you're using leaks, click on the particular "type of leak", then click on the memory address, and it'll show you the alloc/free/retain/release/autorelease history of that memory address. If you enable the detail view (Cmd-E I think), you'll see stack traces for all of those as well. Look for something that's doing a retain but not a corresponding release. (It's a bit difficult when things are retained by multiple autoreleased arrays...)

Using self on iVars in dealloc?

Usually I write my dealloc to look like this:
- (void)dealloc {
[coffeeList release];
[super dealloc];
}
But today I typed (see below) and was a little puzzled why I got an error running the code through the CLANG static analyser. As I say I don't usually add self to iVars in dealloc, but was just a little curious when I spotted this as to what was going on.
- (void)dealloc {
[[self coffeeList] release];
[super dealloc];
}
gary.
I'm just guessing that clang notices [self something] release (or [self.something release]) goes against the memory management conventions. An object returned from a method not having "new", "init", or "copy" in its name should be autoreleased, so releasing it again would appear to be an over-release error.
Because that's really bad form, and goes against how you should be doing things. CSA doesn't just check for semantic errors, it also checks that you're doing things the way you should be. Where the way you should be, is the way things are done in the documentation.
Is coffeeList declared as a property (and set to retain)? Then you should probably do:
[self.coffeeList release];