How to release a "PopUp" view"? - objective-c

I have this class that shows a popup.
I do a alloc-init on it and it comes up.
DarkVader* darkPopUp = [[DarkVader alloc] init:theButton helpMessage:[theButton.titleLabel.text intValue] isADay:NO offset:0];
It shows itself and if the user presses Ok it disappears. When do I release this?
I could do a [self release] in the class when the OK button is pressed. Is this correct?
If I do this the Analyzer says it has a retain count of +1 and gets leaked in the calling function.
If I release it just after the alloc-init the Analyzer says it has a retain count of +0 and i should not release it.
DLog(#"DarkVader retain count: %i", [darkPopUp retainCount]);
says it has a retain count of 2. I'm confused.
In short my question is: How do I release an object that gets initialized does some work and ends but no one is there to release it in the calling function.

My suggestion would be to use
[self autorelease];
when the view is closing itself. Although if you look at various standard views, then all implement callbacks to a delegate that becomes responsible for closing them; this let's the launching object be responsible for releasing the view as well. You also don't make it clear how your view (or is it a view controller) is displayed.

You could do something similar to what existing Cocoa Touch classes does. For example, see how you show an UIAlertView:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"title" message:#"message" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
It's quite clear that UIAlertView does a [self retain], or more likely, gets retained when added as a subview somewhere on the screen somewhere in the show method.
There are some Cocoa Touch classes that indeed (just as Paul mentioned) do not support this way of release at once, but instead calls a delegate method and excepts the receiver to release it.
I'd say the answer is, if your DarkVader is an UIView, you should let the subview-retain take care of the retain count. If it's a UIViewController or a custom helper class, you have a few options, the delegate way being a simple and straight forward one.

If you want a custom pop-up in the style you described you should probably already be subclassing UIAlertView to begin with. Then you can use it's already implemented retain/release functionality.

Related

Opening Modal Sheets with 10.9

I have an app that uses multiple Modal Sheets for data entry. The methods in opening the modal sheets worked fine, and still work fine, but they have been deprecated and I fear they will soon not work with future releases of Xcode. Here, Apple points out how to use modal sheets,
- (void)showCustomSheet: (NSWindow *)window
// User has asked to see the custom display. Display it.
{
if (!myCustomSheet)
//Check the myCustomSheet instance variable to make sure the custom sheet does not already exist.
[NSBundle loadNibNamed: #"MyCustomSheet" owner: self];
[NSApp beginSheet: myCustomSheet
modalForWindow: window
modalDelegate: self
didEndSelector: #selector(didEndSheet:returnCode:contextInfo:)
contextInfo: nil];
// Sheet is up here.
// Return processing to the event loop
}
but with the release of Xcode 5.1, they identify that the loadNibNamed method has been deprecated and that we should use a similar function referencing top-level objects.
The problem I am having, is changing this:
[NSBundle loadNibNamed:#"OrderDetailsWindow" owner:self];
into this.
NSArray *array;
[[NSBundle mainBundle]loadNibNamed:#"OrderDetailsWindow" owner:self topLevelObjects:&array];
This method call does in fact open the modal sheet. However, at the end of my method that opens the modal sheet, Xcode hangs-up with this error.
0x7fff8c33b097: andl 24(%r11), %r10d Thread1: EXC_BAD_ACCESS (code:EXC_I386_GPFLT)
I'm not sure what this is telling me. It doesn't give me any information in the debug area. Could this have to do with the topLevelObjects array not being released properly? Any thoughts on how to make this work a little more smoothly? Apple's out-of-date library is driving me nuts!
Yes, Apple's documentation is a mess. The "Sheet Programming Topics" document has not been updated since 2009.
You don't show the full code after the change but my guess is that your problem is with the memory management of your NIB's objects.
From the documentation of the new loadNibNamed:owner:topLevelObjects:
Unlike legacy methods, the objects adhere to the standard cocoa memory
management rules; it is necessary to keep a strong reference to them
by using IBOutlets or holding a reference to the array to prevent the
nib contents from being deallocated.
Outlets to top-level objects should be strong references to
demonstrate ownership and prevent deallocation.
You have the NSArray that holds the top level objects inside your method. Once the execution leaves this method, the NSArray will be derefernced and released and so are all your top level objects if those are not strongly referenced anywhere else.
You need to either connect your top level objects in the NIB to outlets in your Window Controller or keep the NSArray as a member variable of your Window Controller instance, so it doesn't get released once your sheet showing method exits. And make sure that myCustomSheet properly declared and connected from the sheet's NIB.
Also, [NSApp beginSheet:] is deprecated as well, you now call beginSheet on an instance of NSWindow.
I always use a NSWindowController subclass with a custom delegate for my sheets:
From the window that wants to display the sheet:
_myModalController = [[MyModalController alloc] init];
_myModalController.delegate = self;
[_myModalController beginSheet:self.window];
Then within the modal window controller, I have:
- (id)init {
self = [super initWithWindowNibName:#"MyModalWindow" owner:self];
return self;
}
- (void)beginSheet:(NSWindow *)mainWindow {
[NSApp beginSheet:[self window]
modalForWindow:mainWindow
modalDelegate:self
didEndSelector:#selector(_didEndSheet:returnCode:contextInfo:)
contextInfo:nil];
}
- (void)endSheet:(NSWindow *)mainWindow {
[NSApp endSheet:[self window]];
[[self window] orderOut:mainWindow];
}
This appears to avoid the whole issue of loadNibNamed: becoming deprecated.

NSObject extending UIAlertViewDelegate crash?

I have a helper class that I want to display a UIAlertView then will perform some web service action. I would like to do it this way so I can re-use the alert view in other places.
I am declaring it like so:
#interface FBLinkHelper : NSObject <UIAlertViewDelegate>
I also have a method that will show the alert view:
- (void) showLinkDialog {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Link account with Facebook?" message:#"Linking your account with Facebook extends your user experience. Link account?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[av show];
}
This is how I am showing the dialog:
FBLinkHelper *fbLinkHelper = [[FBLinkHelper alloc] init];
[fbLinkHelper showLinkDialog];
All seems pretty normal to me. It shows the alert view dialog, but when I click a button (either one) my app crashes without much to say in the console. I notice it only crashes when I have a UIAlertViewDelegate method in place, such as didDismissWithButtonIndex. Why is this happening?
-UPDATE-
The FBLinker instance was not a strong property of its caller, so the delegate callback went to a dangling pointer. OP reports making FBLinker strong took care of issue.
Original suggestion of making the class extend from UIView instead of NSObject had no effect, and is referenced here only for historical purposes.
I had the same case in an app I'm working at. If the delegate object isn't a strong (or retain in iOS prior to 5.0) property it might get deallocated right after the UIAlertView is shown.

releasing UIViewController subclasses does not actually free up memory

I have a UINavigationController object (named LoginNav) which consists of ViewController1 & ViewController2, my iPad app starts by loading a UISplitViewController subclass (named mainSplitViewController) and then presenting LoginNav modally on top of it (this is by the way done in didFinishLaunchingWithOptions method of AppDelegate like this:
[self.mainSplitViewController presentModalViewController:LoginNav animated:YES];).
Once ViewController1 is shown, I tap a UIButton in it to push ViewController2, when I finish working in ViewController2 I tap a UIButton in it to call [self.navigationController dismissModalViewControllerAnimated:YES]; to dismiss LoginNav with both of its view controllers and show mainSplitViewController's contents.
There is a dealloc method in both ViewController1 & ViewController2 with NSLog statement in each one, once loginNav is dismissed, the NSLogs never get fired, but doing [self.navigationController.viewControllers objectAtIndex:0] release]; & [self.navigationController.viewControllers objectAtIndex:1] release]; right after [self.navigationController dismissModalViewControllerAnimated:YES]; fires both NSLogs.
I commented out the above two release statements, then I launched the Allocations instrument, and launched the app again and pushed ViewController2 then dismissed loginNav as described above, and looked at Live Bytes column (All Allocations value ) it was 6.9 MB right after the dismissal of loginNav, then I did this step again but in this case using the two release statements, I got exactly a 6.9 MB value on Live Bytes column.
Two Questions:
1) why do not the dealloc methods of ViewController1 & ViewController2 never get fired after the dismissal of the navigation controller LoginNav that holds them ? and is it correct to do the above two release statements to release these view controllers ?
2) why releasing ViewController1 & ViewController2 does not free up memory ?
p.s. there is no single variable (or IBOutlet) being held in memory in both ViewController1 & ViewController2, everything is released in both of them.
These kinds of issues are nearly impossible to troubleshoot without seeing all your code. When you manage memory manually, there are multiple areas that you can go wrong. For example the following code will leak:
- (void)didSelectSomethingInViewControllerOne
{
ViewController2 *vc2 = [[ViewController2 alloc] init];
[self.navigationController pushViewController:vc2 animated:YES];
}
In this case you have allocated the object and thus have ownership of it. Then the nav controller takes ownership of it. When you pop the controller from the navigation stack, the navigation controller relinquishes ownership of it, but you never did, so it still has a retain count of 1 and won't get deallocated.
Relinquishing ownership of the controllers later in your code (like after dismissing the modal view) is a bad idea. It makes it difficult to analyze ownership when your releases are all over the place. As soon as the navigation controller has ownership you can release the object you allocated, as you do not intend to use it in the future:
- (void)didSelectSomethingInViewControllerOne
{
ViewController2 *vc2 = [[ViewController2 alloc] init];
[self.navigationController pushViewController:vc2 animated:YES];
[vc2 release];
}
The situation above can have nothing to do with your problem. Your problem may reside in many different areas. Which is why troubleshooting memory management problems is difficult. Without seeing source code.
Consider transitioning your project to ARC:
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
Another thing you might have fallen foul of is a retained property (such as a delegate) in your LoginNav.

UIAlertView "non-blocking" show message does retain the view?

I have a simple question for you... I was reading Beginning IOS 4 Development book and there is the following code example:
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#”Hello”
message:#”This is an alert view”
delegate:self
cancelButtonTitle:#”OK”
otherButtonTitles:nil];
[alert show];
[alert release];
Then it points out that the show message shows the alert view but the code does not stop it's execution until the user dismisses the dialog, it goes on and executes the following code... since next to the show message there is a release message, does the show method retain the view until it is dismissed? Otherwise I should not release it after the show message has been sent.. I'm sorry but I did not find this information on the reference pages, so I hope this is not a (too much) stupid question.
OT: how do I activate colors on code snippets?
The window that displays the alert view retains its reference, so you don't have to.
I've wondered this and concluded that something in the show method, likely a call to addSubview: increments the retain count on the UIAlertView preventing it from being dealloc-ed.

Why is this object being deallocated?

I'm developing an iPhone app, I'm trying to push a view into the navigation controller, which I've done many times before, however, I'm having some issues with this particular app. I have a table view, and when the user selects one row the new view is pushed into the controller:
DataWrapper *row=[[self.rows objectAtIndex:[indexPath section]] objectAtIndex:[indexPath row]];
DataViewController *nextController=[[DataViewController alloc] initWithNibName:#"Data" bundle:[NSBundle mainBundle]];
[nextController setInfo:row];
[nextController setRow:[indexPath row]];
[nextController setParent:self];
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
and it goes fine, until the user taps the back button, I get an exception, and used NSZombieEnabled and get this:
-[DataViewController respondsToSelector:]: message sent to deallocated instance 0x4637a00
So i tried to remove the [nextController release] and in fact it worked, but WHY???? I allocated nextController, so I'm supposed to release it, right?? I don't feel right releasing this app if there's something like this, I feel like it's going to fail. Please let me know your thoughts.
Your nextController isn't being retained by navigation controller. If you release it then because there is only one init/release pair, the object is deallocated. Later when the navigationController attempts to send messages to it, you get the error you see.
This is also why remove [nextController release] fixes the problem.
You are right in that if you allocated, you should free it. But the caveat is only after your application is done with it, not before.
Some objects will stay allocated for nearly the lifetime of the application, so don't feel too bad.
I would guess that [self.navigationController] is returning nil, because if it weren't nil, it would be retaining your object. Since your object is not getting retained, it would appear that there is no object that's trying to retain it, indicating that the navigationController property is empty.
Is it possible that your navigation controller is somehow being deallocated, resulting in the view controllers also getting released? You could maybe test it by retaining the nav controller just before pushing nextController.
For debugging purposes, I would override -dealloc in your DataViewController class, and set a breakpoint on it.
but it seems to work for something like:
DetailViewController *controller = [[DetailViewController alloc] initWithNibName:#"SomeView" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController: controller animated:NO];
[controller release];
so, why is it not working for pushViewController ?