Is [self autorelease] acceptable? - objective-c

There are cases where you want to present an alert style view controller using your own animations (instead of using presentModalViewController:animated: or UIAlertView).
The right way of releasing this view controller would be in a callback called when the view disappears, but setting up a delegate and all that seems overkill.
So I do this:
- (void)dismiss
{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
[self autorelease];
}];
}
and the presenting object would not release or autorelease the view controller. Memory management wise I see no issue with this. Is it bad practice?

I would consider this bad practice.
Only objects that have called retain on this object should call release or autorelease on it.
I do assume that you haven't called [self retain]
I'm not even sure this would work as you expect it to. Can you guarantee that self would need releasing at that point and why couldn't an object that has retained it called release itself?. Are you trying to force self to dealloc?
If self was deallocated at this point, any other object that was expecting self to still be alive would be passing messages to nil, or worse, the memory might be re-allocated and those objects would be sending messages to arbitrary objects.

It seems like bad practice however don't see any effect on memory. In terms of modal view why don't you just pop it back if thats what you want to achieve using:
[self.navigationController popViewControllerAnimated:YES];

Related

EXEC_BAD_ACCESS while popping out a new View inside existing View

I am trying to call a new view inside my existing view
TransactionFinish *childView= [[TransactionFinish alloc] initWithNibName:#"TransactionFinish" bundle:nil];
childView.view.frame = self.view.frame;
childView.view.frame=CGRectMake(10, 10, self.view.frame.size.width-20, self.view.frame.size.height-20);
childView.view.alpha = 0.0f;
[self.view addSubview:childView.view];
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseOut
animations:^{
childView.view.alpha = 1.0f;
}
completion:^(BOOL finished) {
}];
It is going inside ViewDidLoad() of TransactionFinish(I have tried debugging it) but it gives me Thread EXEC_BAD_ACCESS(code=1,address=0x31f54e62) with green color
The issue lays with the lifetime of childView. You instantiate and store a reference to it into a local variable:
TransactionFinish *childView= [[TransactionFinish alloc] initWithNibName:#"TransactionFinish" bundle:nil];
If you are using ARC, when the local variable goes out of scope, the object referenced by it (childView) is deallocated.
If you are not using ARC, I suppose you are doing:
[childView release];
somewhere to avoid childView to be leaked (as the code you pasted above would imply).
Either hypothesis would explain why you get the crash: when viewDidLoad is called, the controller has already been deallocated.
Adding childView view to self.view:
[self.view addSubview:childView.view];
will retain childView.view but not childView. So the controller is deallocated, while its view is not.
A fix to this is creating a strong property in your class to store a reference to your childView controller:
#property(nonatomic, strong) TransactionFinish *childView;
Another possibility is using controller containment; you could do something li
[vc willMoveToParentViewController:self];
[self addChildViewController:childView];
[self.view addSubview:childView.view]; // or something like this.
[childView didMoveToParentViewController:self];
but this will only work on iOS5+.

UIViewController and UITableViewController, how to change self.view then back again?

I dont want to add a sub view, but instead change the "self.view" to another view eg (A warning view) then after the user suppresses the warning I would like to switch back. When ever i try to switch back to the original view i just get a blank screen for reasons i cant understand.
Here is what i currently have in one of my UITableViewControllers
//Show warning view controller
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view;
//Then later
self.view = self.tableView; //<< Dosnt work
If you want to change your view, and if the original view is defined/linked into XCode, you must retain it before changing self.view to another view. If not, the original view is released and using it back can cause bad things to happen.
Warning :
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view
is a bad bad call. Because you autorelease the controller but you use its view. So you get a view retained with a released controller after some time. Retain the controller and release it yourself when its view is not needed anymore.
Here's the better way to do what I think you're trying to do:
WarningViewController *warningViewController = [[WarningViewController alloc] initWithNibName:#"theRightNiborNil" bundle:nil];
[self presentModalViewController:warningViewController animated:YES];
// or if you don't need to support iOS4 any more:
[self presentViewController:warningViewController animated:YES completion:nil]
// and if you aren't using ARC yet, then [warningViewController release];
Then in your WarningViewController you want some action that calls:
[self dismissModalViewControllerAnimated:YES];
// or again if this is iOS5..
[self dismissModalViewControllerAnimated:YES completion:nil];
Hope that helps.

difference between [self.view release] and [view release]?

What is the difference between
difference between [self.view release] and [view release].
I get a memory leak error in [self.view release]
[view release] is calling NSObject's - (void)release method directly on an instance variable named view.
[self.view release] is calling - (UIView *)view (I'm assuming view is of type UIView) on self and then calling - (void)release on the returned object.
There shouldn't be any practical difference. However, since one reason for using properties is to eliminate the need for explicit memory management, [self.view release] would be a strange thing to do. If it's a property, let the property accessors retain and release the object as necessary.
It's hard to see why you'd get a real leak from [self.view release]. Leaks come from failing to properly release an object -- I don't think you can create a leak by releasing something. It may be that accessing the object through a property prevents the static analyzer from connecting the dots from +alloc to -release.

Can I call [self retain] within -dealloc? Or, how do I ensure dealloc happens on the main thread?

Here is my situation. It's complicated so bear with me.
I have a view class, let's call it MyView. It creates a loading indicator subview, and then starts a background operation that will load data. It also creates a block that the background queue operation will enqueue on the main queue when it's done. The block prepares the view by adding another subview, a UITextView, with the loaded data. Of course, to do that, the block has to have a reference to the view.
So the background operation retains the block, and the block retains the view. With me so far?
Sometimes the instance of MyView is removed from its superview before the background queue operation is finished. And sometimes the main queue operation, which calls the block, gets completely cleaned up before the background queue operation gets completely cleaned up. In this case, the instance of MyView can get its -dealloc call on the background thread, because the last reference to the view belonged to the block, and the last reference to the block belonged to the background operation.
UIKit doesn't like to be called from any thread but the main thread. In the case of UITextView, apparently that even includes -dealloc calls. I get EXC_BAD_ACCESS from something called the "web thread lock" during the -dealloc of the text view.
I think it's reasonable for the background thread to have the last reference sometimes, and I would like to handle this from within my -dealloc implementation, like so:
- (void)dealloc {
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
// The usual -- dealloc subviews safely on the main thread
self.myIvar = nil;
[super dealloc];
}
else {
// Not on the main thread, so keep the object alive
// in spite of the dealloc call.
[self retain]; // explicit retain
[[NSOperationQueue mainQueue]
addOperationWithBlock:^{ // implicit retain at block creation
[self release]; // explicit release
}]; // implicit release, dealloc called again, but on the main thread
}
}
So when you call -release on an object, the implementation in NSObject calls -dealloc if the retain count reaches zero. Is that all that happens? In other words, is it OK to get a call to -dealloc and not call super? Am I making some kind of abominable zombie object or is this fine?
If this is not OK, what is a good way to make sure -dealloc gets called on the main thread?
Why not just override release?
- (void)release
{
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(release) withObject:nil waitUntilDone:NO];
} else {
[super release];
}
}
Edit: This was pre-ARC. Don't do it with ARC.
Why not just avoid passing your view to the block that's executed on a background thread? You could have the controller register for a notification, and have the block issue the notification on the main thread's notification center:
NSNotification *notification = [NSNotificationnotificationWithName:#"data upated" object:nil];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:notification waitUntilDone:NO];
Ive seen this approach used:
- (void)dealloc {
if (is on main thread) {
[self _reallyDealloc];
} else {
[self performSelectorOnMainThread:#selector(_reallyDealloc)];
}
}
- (void)_reallyDealloc {
[ivar release];
[super dealloc]
}
This still isn't ideal (and breaks in ARC). The best answer is to guarantee that the final release happens on the main thread.
pardon the pseudo code. Typing code on an iPhone is sub-optimal
Once dealloc is called, it's too late. That object will go away, no matter what you do. If the problem is that dealloc does things that are not safe to do on a background thread, then dealloc can dispatch these things to the main queue, where they will be performed when the "self" is long gone.
Overriding "retain" is quite perverted. Let's say it's not something you should do when you ask for advice on stack overflow.com. And then there's ARC where you can't do this anyway.

Memory Management

How is method removeFromSuperView: really works?
I got a problem of memory bad access when I want to reinit the view
- (id)init {
if (!(self = [super init]))
return nil;
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
NSLog(#"retainCount :%d", [_mainView retainCount]);
UIButton *reInitButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f,0.0f,90.0f,35.0f)];
[reInitButton addTarget:self action:#selector(buttonDidTapped:) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubView:_mainView];
NSLog(#"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(#"retainCount :%d", [_mainView retainCount]);
return self;
}
- (void)buttonDidTapped:(id)sender {
[_mainView removeFromSuperView]; //crash during second times press the button
NSLog(#"retainCount :%d", [_mainView retainCount]);
_mainView = [[UIView alloc] initWithFrame[[UIScreen mainScreen] bounds]];
[[self view] addSubView:_mainView];
NSLog(#"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(#"retainCount :%d", [_mainView retainCount]);
}
I have NSLog every times there are any retain or alloc or release keyword. And the result is very weird.
//init
retainCount : 1
retainCount : 2
retainCount : 1
//1st time pressed button
retainCount : 1 //remove super view didn't decrease
retainCount : 2
retainCount : 1
//2nd time pressed button
retainCount : 0 //crash. Memory bad access
The weird thing is why it didn't crash on 1st time pressed??
I think your problem is here:
[_mainView release];
You've dropped your reference to _mainView, and yet, by my reading, that's a member variable that you'll keep around and continue to invoke methods on. That's not valid. Once you've called -release, you've essentially told the system you're not going to use that object again, and you can't do anything useful with a stale pointer to that object, like you do when you call -removeFromSuperView on it later.
If you want to continue to keep _mainView around and call code on it, you need to keep a reference. Perhaps you should move the release to your object's -dealloc method. Alternatively you could -release it in the button method and re-create a new view the next time you need to.
As a helpful tip, a lot of programmers like to reset objects to NULL (or nil in objC-speak) after releasing them, as a reminder that you can't use that object again. If you -release something, you'd better mean it.
Lastly, I suggest you Google the term "reference counting" and read up on it; it's a more generic idiom than the specifics of NSObject, and it is likely to be useful to think of the basics and how you might implement this in another language, like say, C. This will help you reason better about reference counted objects.
NEVER USE RETAINCOUNT. Sorry for putting that in caps, but I can't figure out for the life of me why people still use it. It's a faulty reference for memory management. Use instruments or similar instead.
You shouldn't be accessing _mainView at that point. This may be hard to explain, so bear with me. We're going to count, but not absolute retain count, just your code's claims on the object.
You allocate memory for an object and point at it with _mainView:
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
You have 1 claim of ownership to that object. When you add it as the subview of another view, that view likewise makes a claim of ownership, but that's not yours, it's the view's. The fact that it makes the object in _mainView stick around is an accident, and you shouldn't rely on it. Then you release the object:
[_mainView release];
You have relinquished your ownership claim -- you now have 0 claims, and you should no longer try to access this object. You don't own it. Again, the fact that it still exists because another view is using it, and the fact that you still have a pointer to it, are accidents*, and you should not rely on them.
When it comes time to handle your button press, then, you are accessing an object over which you have no ownership:
[_mainView removeFromSuperView];
and this causes a crash, which may not be expected, but it is not unreasonable. By letting your claims of ownership go to 0, you told the system "I don't need this object anymore. I'm not going to access it after this point. If it disappears, I will not be affected." In fact, though, you do need it to stay around, and you do need to access it.
What you should do, then, is move the line:
[_mainView release];
to inside the button action, right after the call to removeFromSuperview.
*The second of which could be avoided by setting _mainView = nil; after you release it, in this case, but that won't solve the greater problem.