[self retain], [self autorelease], is this correct when I'm assigned as a delegate? - iphone

I solved a crash with something that sounds weird do me : calling [self retain] and [self autorelease].
Here's the case :
MyObject is a subclass of a UIView, so usually it's allocated, put on the view stack, and released. MyObject also has a timer, which will remove itself from the superview. So basically MyObject can be deallocated at anytime.
Apart from displaying cool stuf, MyObject is also able to displays a UIAlertView, and waits for the user's choice, so it's the alertView's delegate.
The problem is that if the user makes a choice after MyObject is deallocated... well you know EXC_BAD_ACCESS I guess...
So I could have kept a reference to the AlertViews (yes, there are severals), and set the delegate to nil in the MyObject dealloc method. But I couldn't process the choice then (and as there are several, it would make more variable instances, which I don't like.
So what I did is :
//alertView creation
-(void)showAlert{
[self retain];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"title"
message:#"message"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes",nil];
[alertView show];
[alertView release];
}
//Delegate method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[self autorelease];
// Do the job
}
I know it's not nice, I've put many comments around this to make other developers that will reuse the code be careful, but I can't see another option. Is it correct ? Is there a better way todo it ?

Keep in mind that the delegate pattern, in order to avoid having a cyclic dependency which prevents the main object as well as the delegate from being released since they both have a reference to each other, requires that the main object does not retain the delegate. So if you create a delegate on the fly and assign it to an object, you have to retain and autorelease it yourself!

Frankly, your program structure is all gnarly, and that's what is causing your trouble. You should move all of the delegate functionality and the responsibility for managing the views into a separate controller object.

First of all, I don't see a problem with retaining self. I also did it in some multi-threaded code. Maybe not the best solution but it worked.
Now, if you want to avoid [self retain], consider the following:
Maybe instead of straight deallocating when the timer fires, you instead check if there's an Alert presented. If not, release. If yes, hide the view (but don't remove from superview) and set a flag in a new ivar that the object should be removed from superview. In the sheet's callback, check the flag and if set, remove the view from the superview, releasing it.

Can't you make your alertView an #property or instance variable and just set alertView.delegste = nil in MyObject's dealloc?

Related

UIView and memory management

A beginners question about how to be memory efficient when using an UIView which contains a couple of images (ca. 500K). I guess if I handle this in the wrong way and call this view ten or twenty times, my app will crash (as I have leaked about 5-10 MB of RAM).
I have an UIView which I create programatically like so:
myView = [[UIView alloc] initWithFrame:0,0,0,0];
To this view I add a couple of images so that it eats up 500K of memory. After I'm done with this view, I'd like to free up the memory again. So I coded:
[myView removeFromSuperview];
myView = nil;
[myView release];
Is this the way to go? I am particularly uncertain about the last release call. Is myView not already released if I remove it from my superview and set it to nil?
Also, would it be a good idea to simply autorelease myView in the first instance, i.e.
myView = [[[UIView alloc] initWithFrame:0,0,0,0] autorelease];
I'd be grateful for any suggestions and corrections.
You’re sending a release message to nil. The correct order for those statements would be:
[myView removeFromSuperview];
[myView release];
and optionally after that:
myView = nil;
For discussion on why to set to nil:
Set pointers to nil after release?
Is It Necessary to Set Pointers to nil in Objective-C After release?
What's the difference between setting an object to nil vs. sending it a release message in dealloc
The superview retains your view when you add it as a subview, and then releases it when you remove it. You still need you release your hold of it. You could use autorelease when allocating it, but since you need to hold on to a pointer to it to be able to send removeFromSuperview, the correct way is to send release when you are done with that pointer (and then set that pointer to nil).
If you set your view to nil before you call release, you will leak the view and then send a message to nil. First you must release the view:
[myView removeFromSuperview];
[myView release];
Then you can set your variable to nil to avoid sending a message to a deallocated instance.
About the autorelease, I think it's just a matter of personal preference, but I find it much easier to track memory issues when doing:
myView = [[[UIView alloc] initWithFrame:0,0,0,0] autorelease];
// add myView to wherever it belongs
.....
[myView removeFromSuperview];
myView = nil;
As others have pointed out, settting to myView to nil before you call release is incorrect and will leak memory.

Silly memory management issue that has me stumped

I have a property defined as:
#property(nonatomic, retain) UITableView *settingsTableView;
Then in my viewDidLoad method I have:
self.settingsTableView = [[[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped] autorelease];
[self.view addSubview:self.settingsTableView];
[self.settingsTableView release];
Then in the dealloc method of the view controller I have:
[settingsTableView release];
When I attempt to do the release from within the dealloc I am getting a "message sent to deallocated instance". I am starting to second guess myself, anybody see anything stupid in what I've done?
Really appreciate the help on this one!
you're calling release on an object you've already autoreleased. Just get rid of the line
[self.settingsTableView release];
and you should be good.
Note that you should keep the release in the dealloc method, since the property calls retain for you, but not release.
Two things. First, you're over-releasing the table view in the first place: the autorelease call negates the need for a manual release afterwards.
Also, in general, what you release in -dealloc are things that you created in -init, -initWithCoder:, or whatever, not loadView or -viewDidLoad. In this case, the method you're looking for is -viewDidUnload; you just have to set self.settingsTableView to nil in that method, and the property setter will handle releasing it if necessary.
Here is the change you need to make.
self.settingsTableView = [[[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped] autorelease];
[self.view addSubview:self.settingsTableView];
[self.settingsTableView release];
//^^^ This line is bad no need to release this value until dealloc
//if it is defined as retain or copy
Seems obvious. You have already released with
[self.settingsTableView release];
So why release it again in the dealloc?
I believe the problem is related to your use of autorelease when you are allocating and initializing the UITableView.
You also might have a problem with releasing the settingsTableView right after you use it, vs. in the dealloc method. Anytime you alloc/init an object, you should only release it once.
If you use autorelease, the rules are a little different, so I'd recommend reading up on that again. Also, when you pass objects that you've created off to other things, they may take complete or shared ownership of the object by retaining it, meaning that you may or may not need to release it yourself anymore. The documentation for that should be on the method you're calling (like addSubView).

What's the difference between a ModalViewController and NonModalViewController in Iphone?

The difference of look between the two controllers.
And
Game *game = [[Game alloc] initWithNibName:#"mygame" bundle:nil];
I try to put a autorelease at the end of line. the program crashed.
Therefore I am wondering if
[self presentModalViewController:game animated:YES];
would increment reference count by 1?
[self dismissModalViewControllerAnimated:YES];
would decrement the reference count by 1?
After presenting the controller just release it normally, make sure you don't send messages to game after releasing it though. presentModalViewController:animated: increases it's retain count so you are able to release it with out deallocating the object and you can successfully pass ownership to the current view controller.
Game *game = [[Game alloc] initWithNibName:#"mygame" bundle:nil];
[self presentModalViewController:game animated:YES];
[game release];
Then when it comes to dismissing it you shouldn't retain or release, just call the dismiss method.
Don't necessarily think about the retain count (and whatever you don't call the retainCount method on anything and decide what to write based on when is returned, that method is for legacy purposes only). Just match every init/new/copy with a release/autorelease.
(See listing 6-1 on this Apple doc, for prove that you should release it)
Effectively the [game release]; counteracts the init... and the dismissModalViewController... counteracts the presentModalViewController...
No, presenting a view controller only does that, it presents it. You should be calling retain and release at the appropriate times. It's important to keep track of your retain counts.
Keep in mind that when you allocate and initialize, the retain count increases by one. You will have to release that object later.
If you're still unsure, you need to read the Apple docs on view controllers and memory management.

How do I call and app deletage method (which uses an alertview) and get the value in view controller after the events?

I'm calling a method I've written in app delegate which calls an alertView to ask the user for a password, I use the method in applicationWillEnterForeground also.
So the value is set in clickedButtonAtIndex in app delegate.
How do I determine when the value has been set in the view controller (where I'm calling the method) ?
The simplest way to do this would be to delegate back to your viewController.
Your app delegate will have the ability to access the pointers to any ViewControllers/navControllers that you have. (scope depends on your design of course)
Here is a good Post on objective-c delegates.
I found myself doing this kind of thing quite often to simplify the process i had a method contained in a singleton that i called 'toolbox' that went something like this.
-(void)showAlertWithTitle:(NSString*)_title Message:(NSString*)_message CancelButtonTitle:(NSString*)_cancelTitle AlternitiveButtonTitle:(NSString*)alternitiveButtonTitle AndDelegate:(id)del
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:_title message:_message delegate:del cancelButtonTitle:_cancelTitle otherButtonTitles:alternitiveButtonTitle, nil];
[alert show];
[alert release];
}
This meant that i could call an alert where i wanted and have the Alert delegate back to where i wanted, then just implement UIAlertViewDelegate there.

UINavigationController and UIViewController dealloc

I recently changed my app to use a UINavigationController, I was using a UINavigationBar before, with cascade subView adding, which was a bit tenuous.
I'm facing a problem of memory usage. Leaks tool doesn't show any leak, but ViewControllers I create and add to the UINavigationController never seem to be released. So memory usage grows everytime I create a new VC and then press the NavigationController's back button.
I simply create and add my VCs this way:
DetailViewController* detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
// setups
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
The app never goes through ViewController's dealloc and viewDidUnload methods. Shouldn't these be called everytime I press the back button?
I've searched many tutorials and read Apple's memory management, but there's nothing about VC's lifetime in memory when using NavigationController.
Maybe you are not doing something wrong and instead you are facing something like this
In the Blog post it was the question whether we have to manually release IBOutlets or not. As it turns out we should. This was reproduceable in iOS 3.1.3 but I didn't test it in iOS 4.0 yet.
The second aproach is to override your view controllers retain and release method and print out the retain count. I had a simimlar problem, that some view controllers dealloc method did not called so I override this methods to see wether someone has still a retain on it. As it turns out it did.
Edit:
When I printed my retain count, it would sometimes reach ~98 caused from the framework, so thats not really to worry.
If your last retain count stays at 2 and the dealloc method won't be called, than there is someone that has still a retain on it.
In this case you should search on other places.
For example another problem I encountered during this same problem:
Sometimes I would use
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateUI) userInfo:nil repeats:YES]
to constantly update the UI. But what I forgot was, that the NSTimer will retain the target object (which was the ViewController). Because the NSTimer retained your view controller your dealloc will never be called because someone (NSTimer) has still a retain on it. So you have to make sure to invalidate the NSTimer BEFORE dealloc method to properly release the view controller.
Edit2 in response for a comment below:
A retain declaired property does as follows (exsample):
- (void)setTarget:(id)value {
if (value != target) {
[target release];
target = [value retain];
}
So it does first release your current self.target then retains the new value. Since you are assigning nil your target will be nil afterwards. Further info about Properties can be found in the Apple doc.
I have seen this as well. As you pointed out, I haven't seen anything definitive in the docs, but my belief is that they are retained in memory until memory is needed. It makes sense from a performance perspective as doing so allows the app to quickly navigate between the different views.
Bottom line is, I wouldn't worry about it. You could fire off some Low Memory Warnings in the Simulator and see if it actually releases your VCs.