I am working on an application where i am pushing one view controller on to a UINavigationController and releasing it immediately as the navigation controller retains it.When i am poping the view controller the dealloc method is being called as expected but the problem is the app is getting crashed.If i observe in GDB by enabling NSZombie its saying that -[MyViewController isKindOfClass:]: message sent to deallocated instance 0x6847a00.If i remove [super dealloc]from my view controller's deallocmethod its working just fine.I have nothing else in dealloc method except [super dealloc].What could be the problem here, can any one please help.The code snippet is below:
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:nil bundle:nil];
myViewController.path = selectedPath; //very important to set path here
myViewController.parentViewController = self;
[self cleanBookshelf];
[self.navigationController pushViewController:myViewController animated:NO];
[myViewController release];
[indicatorView removeFromSuperview];
[loadingindicator stopAnimating];
and i am poping in one action method of myViewController as
-(IBAction)goBack:(UIButton*)sender{
[self.navigationController popViewControllerAnimated:YES];
}
Just guessing, but I suspect that the problem is with this line:
myViewController.parentViewController = self;
UIViewController's parentViewController property is marked readonly, and I'd take that as a strong message that you shouldn't mess with it.
Related
Pushing my view controller twice to navigation controller and simulating memory warning, lead to application crash with error: "message sent to deallocated instance"
I'm pushing view controller by pressing on button:
-(void)buttonPressed
{
MyViewCOntroller *vc = [[MyViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
I'm using ARC.
Scenario:
[self buttonPressed];
Press back button in vc.
[self buttonPressed];
Press back button in vc
Simulate memory warning
The crash does not happen if pushing only once.
I tried also to move "vc" to be ivar of parent controller, but the effect is the same...
Maybe this will help, but I'm using custom back button and its selector:
-(void)backButtonPressed
{
[self.navigationController popViewControllerAnimated:YES];
}
create this vc object as class member and check if crash exists
#interface ParentViewController()
{
MyViewCOntroller *vc;
}
#end
#implementation PaintingViewController
-(void)buttonPressed
{
vc = [[MyViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
Found a solution to this. Please see my answer to my other question:
[UINavigationController retain]: message sent to deallocated instance
I'm using ICETutorial with cocoapods.
I'm using it in a SettingsViewController where you can view the tutorial in settings.
// SettingsViewController.m
Tutorial2ViewController *vc = [[Tutorial2ViewController alloc] init];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController pushViewController:vc animated:NO];
And Tutorial2ViewController inherits from ICETutorialController
#interface Tutorial2ViewController : ICETutorialController
ICETutorialPages have buttons that will trigger a callback. It takes in a block. So in my implementation, I have this:
- (id)init
{
ICETutorialPage *layer1 = [[ICETutorialPage alloc] initWithSubTitle:#"Page 1" description:#"Page 1" pictureName:#"Tutorial1_640x1136.png"];
NSArray *tutorialLayers = #[layer1];
self = [super initWithNibName:#"ICETutorialController_iPhone" bundle:nil andPages:tutorialLayers];
__weak Tutorial2ViewController *vc = self;
[self setButton1Block:^(UIButton *button){
NSLog(#"Button 1 pressed.");
[[vc.navigationController topViewController] dismissViewControllerAnimated:NO completion:nil];
}];
if (self != nil)
{
}
return self;
}
The reason why I put all the code in init is that I don't want SettingsViewController to know anything about how the Tutorial2ViewController works. Settings should alloc and init, push to the navigation controller stack and the Tutorial2ViewController should know how to handle itself.
I do get the NSLog that button1 is pressed but the view controller does not dismiss itself and return me to the SettingsViewController.
I will contact the creator of the library and ask him/her to see this question also. I feel that this is me not misunderstanding blocks, navigation controllers, cocoapods, etc...
Thanks
Just try [self.navigationController popViewControllerAnimated:YES];
I want to display the modalViewController from the 2nd screen of the app, and when I dismiss the controller it should navigate to the 1st screen. The below code works fine in iPhone 4, 5 and iPod Touch but NOT in iPAD. The objective is when I dismiss the modalViewController it shouldn't go back to the second screen, but it should display the first screen.
ShareEmail *shareEmail = [[ShareEmail alloc] initWithNibName:[NSString stringWithFormat:#"%#",xibShareEmail] bundle:nil];
shareEmail.fromWer = #"ownPer";
[self presentModalViewController:shareEmail animated:NO];
[shareEmail release];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
In share email class
[self dismissModalViewControllerAnimated:YES];
You need to retain your shareEmail view controller - make a synthesized retained property
YourClass.h:
#property (nonatomic, retain) ShareEmail *shareEmailViewController;
YourClass.m:
#synthesize shareEmailViewController;
Then display your modal view controller by:
ShareEmail *shareEmail = [[ShareEmail alloc] initWithNibName:[NSString stringWithFormat:#"%#",xibShareEmail] bundle:nil];
self.shareEmailViewController = shareEmail;
[self presentModalViewController:shareEmail animated:NO];
[shareEmail release];
The retained property will keep the view controller from being deallocated while in use. Then when you dismiss it later you can do:
[self.shareEmailViewController dismissModalViewControllerAnimated:YES];
self.shareEmailViewController = nil;
which will release the retained property and free the memory after you're done with it.
Something interesting is happening. You are presenting a view controller from self, and then you're having the navigationController perform its backwards navigation. At this point, the self I was talking about earlier disappears.
Remove the popViewController method from your presentViewController method.
Also, you'll need to use the ^completion handler method. That's where you should put your navigation controller pop code.
-(void)present {
ShareEmail *email = [[ShareEmail...
//You'll need to get a weak reference to `self` in `email`
email.modalDelegate = self; //you need to make a property in ShareEmail
[self presentViewController:email animated:YES completion:nil];
[email release];
}
-(void)dismiss {
[self dismissViewControllerAnimated:YES completion:nil];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
}
//in ShareEmail.m
[modalDelegate dismiss];
Noobie iOS developer with an adopted iOS app here.
I have a settings piece of an iOS app and when the user clicks done, I want the modal view controller (which it currently does) and I want to call a function called updateMainUI in the presentingViewController.
Here's the method I am calling:
- (void)done:(id)sender
{
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; // works
[[self presentingViewController updateMainUI]; //doesn't work
But I get the following error:
No visible #interface for 'UIViewController' declares the selector 'updateMainUI'
But I have declared this in the presenting controller
#interface LocationsViewController : UITableViewController <CLLocationManagerDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
-(void)updateMainUI;
But it's odd that the error should be about UIViewController when the presenting controller is a UITableViewController.
Any ideas on how to get the updateMainUI working? And what I'm doing wrong?
thx
edit #1
This is how I'm creating this - any idea how to get a refernce to the *nearbyViewController?
UIViewController *nearbyViewController = [[LocationsViewController alloc] initWithNearbyLocations];
UINavigationController *nearbyNavigationController = [[UINavigationController alloc] initWithRootViewController:nearbyViewController];
...
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:nearbyNavigationController, browseNavigationController, eventsNavigationController, nil];
After dismissing ones self, you should expect self.presentingViewController to become nil. Try calling updateMainUI just before dismissing.
Crud, I see the real answer now. It's a casting problem. Try this:
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
[(LocationsViewController *)[self presentingViewController] updateMainUI];
You need to be very certain that -presentingViewController returns an object of type LocationsViewController or you will have bigger problems on your hands.
[[self presentingViewController updateMainUI] is broken and won't compile. Assuming you meant [[self presentingViewController] updateMainUI], then there are other ways of doing it.
LocationsViewController *presenting = (LocationsViewController *)[self presentingViewController];
[presenting dismissViewControllerAnimated:YES completion:^{
[presenting updateMainUI];
}];
Using the variable presenting is an important step.
I am using a class inherited from UINavigationController present as a modal view, in the navigation bar I have a button 'Done' which will dismiss the modal view when user tap on it. Everything behave normal except the dealloc() in ImagePickerController, GroupPickerController (which is initialized as root view) not get called when I dismiss the modal view. This cause the leak of the memory.
Here is the code use it:
ImagePickerController *picker = [[ImagePickerController alloc] initWithRootViewController:nil];
// don't show animation since this may cause the screen flash with white background.
[self presentModalViewController:picker animated:NO];
picker.navigationBar.barStyle = UIBarStyleBlack;
[picker release];
Here is what's inside ImagePickerController, which is a UINavigationController:
- (id)initWithRootViewController:(UIViewController *)root {
GroupPickerController *controller = [[GroupPickerController alloc] initWithNibName:nil bundle:nil];
self = [super initWithRootViewController:controller];
[controller release];
if (self) {
self.modalPresentationStyle = UIModalPresentationPageSheet;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
-(void) dismiss
{
[self.navigationController setViewControllers:nil];
[self dismissModalViewControllerAnimated:YES];
}
Here is the code in GroupPickerController, it response to a button in the navigation bar, to dismiss the modal view:
...
#pragma mark - button actions
- (void)done {
[self.parent dismiss];
}
I tried to manually remove the views from NavigationController, seemed not no effect at all...
[self.navigationController setViewControllers:nil];
Thanks for the help!
UPDATED:
Please disregard this question, apparently it's a mistake. :(
Finally get the problem solved... not change any of the code, but a rebuild the project. :(
First of all, you should not be subclassing UINavigationController:
This class is not intended for subclassing.
What does this line do?
controller.parent = self;
If the controller retains the parent-property, you have a retain cycle which would cause the issue you are describing. Remember that all view controllers in the UINavigationController stack can access the navigation controller with the -navigationController property.
There's is a difference between a UIViewController begin dismissed and released.
When you dismiss it, it can be released at any moment, but not necessarily immediately.
Are you sure you have a memory leak ? Maybe the picker is released a few seconds after the dimiss.
If you really have a memory leak, that means there is another place where you picker is retained.