I'm loading a viewController (LandingViewController) that's crashing because my 'ConnectionStatus' object is being deallocated for some reason by UIKit, and I'm trying to send a message to it.
My code is as below:
#property (nonatomic, strong) ConnectionStatus *cStatus;
- (void)viewDidLoad
{
[super viewDidLoad];
...
self.cStatus = [[ConnectionStatus alloc] init];
}
When I load up zombie instruments, I get this:
Is there a reason why UIKit is deallocating the object before my view controller even loads?
UPDATE (how I load this View Controller):
-(void)logout {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
LandingViewController *lvc = [storyboard instantiateViewControllerWithIdentifier:#"landingVC"];
[self presentViewController:lvc animated:YES completion:nil];
}
The viewDidLoad release is correct; that is ARC releasing the object you created and assigned to the strong property.
The problem looks to be to be either in your ProfileViewController, or in whatever notification is being sent. The object is a zombie at the point where you are posting a notification. You may be retaining something incorrectly or using it (or an owning object) as the notification object, but we'd need to see that code to be sure.
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
Im using a page based application very similar to the standard template from apple (page based app). I want all the pages to be loaded and kept in memory at start up, but it doesnt work. I keep all the ViewControllers that UIPageViewController displays in an NSArray which I initiate. The problem is that the ViewControllers are not initilized until the PageViewController calls them. How do I force initiation of ViewControllers right here in the init method of the ModelController.
- (id)init
{
self = [super init];
if (self) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
historyViewController.restorationIdentifier= HistoryRestorationID;
UIViewController *totalViewController = [storyboard instantiateViewControllerWithIdentifier:#"TotalViewSlidingController"];
totalViewController.restorationIdentifier= TotalRestorationID;
PlugTableViewController *plugViewController = [storyboard instantiateViewControllerWithIdentifier:#"PlugSlidingViewController"];
plugViewController.restorationIdentifier= PlugRestorationID;
//Force initiation of ViewController here!
pages = [[NSArray alloc] initWithObjects:historyViewController,totalViewController,plugViewController, nil];
}
return self;
}
The reason why I want to initiate them directly, is because they take time to initiate.
The view hierarchy is not loaded until someone references its view property.
Try call [historyViewController view] or read historyViewController.view. It should do the job.
Try type-casting the object where you want to use this view, hopefully this will do the job.
PlugTableViewController *plugView=(PlugTableViewController *)[pages objectAtIndex:3];
And one more things, try to change the code
UIViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
to
HistoryViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
hopefully you have made a header class HistoryViewController but you are initlizing view by UIViewController
Another possible solution is to pre-load data instead of viewcontroller as view controller do not take time to load.
MOST UNRECOMMENDED way is to add all view as subview and keep them hidden, show them when you need them.
I have a custom ViewController which is an instance variable of my root viewController.
I intend to modally present it whenever a button is touch. Therefore the viewController will be presented and dismissed potentially many many times.
I obviously only want to alloc init my instance variable once as the modal viewController is not deallocated each time it's dismissed, so should I have code like this inside my button action to ensure that it's only alloc and inited once?:
if(!myViewController)
{
ViewController *myViewController = [[ViewController alloc] init];
}
[self presentViewController:myViewController animated:YES completion:NULL];
I usually use lazy instatiation in those cases:
Declare a property for your ViewController:
#property(nonatomic, strong) UIViewController *myViewController;
After that you can override the get of myViewController
-(UIViewController*) myViewController {
if(!_myViewController) {
_myViewController = [[UIViewController alloc] init];
}
return _myViewController;
}
This way you guarantee that was only instantiated once and is always there when you needed.
ATTENTION
This works well if you always use self.myViewController. I consider a good practice that properties' generated iVars should only be accessed in their setters/getters.
You can use the following way to ensure that only one instance of the view controller active at a time.
if(myViewController) {
[myViewController release];
myViewController = nil;
}
myViewController = [[ViewController alloc] init];
[self presentViewController:myViewController animated:YES completion:NULL];
You need to make myViewController as class variable.
I was thinking about this today, and now i've tested i'm a little confused…
When using viewControllers either by pushing a viewController onto the Navigation Stack or Presenting a ViewController modally I'm wondering about memory management.
Lets use the modal example as a thought experiment, here is the source to create and present the view, in my example it doesn't matter if ARC or not so here's both:
With ARC:
ViewController *myViewController = [[ViewController alloc] init];
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];
Without ARC:
ViewController *myViewController = [[ViewController alloc] init];
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];
[myViewController release]; //As it's now 'owned' by the presenting View controller
This would be my understanding about how to present a viewController modally over an existing ViewController.
Lets say for our example the above code resides in a method which is called when a button is touched to present the ViewController.
Now to my question,
What I am doing is calling this code each time a button is touched, During testing with Instruments I didn't seem to have any leaks. - However because I have NSLog statements in the myViewController dealloc & viewDidLoad methods I know that it's getting instanciated everytime I touch the button but never deallocated.
So...
A) Why am I not getting a leak showing (or a rise in Live Bytes) in instruments (when either using ARC or not) because I am seemingly creating a new viewController and leaking the old one each time I go to present it.
B) What is the correct way to write the above code if this is not memory safe? I see this kind of code snippets all over Apple's example code and internet. Should I (and they) not be wrapping the alloc init line in an if statement to check if the object is already created?
i.e.
if(!myViewController)
{
ViewController *myViewController = [[ViewController alloc] init];
}
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];
Thanks for taking the time to read and answer, I really wonder about this as I've been creating, pushing and presenting ViewControllers using the above code the whole time, and never noticed a leak! - might have to go back and rewrite it all!
To avoid confusion please note: The delegate property is a custom property of my UIViewController subclass (where I've implemented a delegate protocol), required to dismiss the Modally present Viewcontroller properly. As per coding guidelines.
Regards,
John
EDIT As Requested, Creation of the delegate:
.h
#protocol NotificationManagementViewControllerDelegate;
#interface NotificationManagementController :
{
__weak NSObject <NotificationManagementViewControllerDelegate> *delegate;
}
#property (nonatomic, weak) NSObject <NotificationManagementViewControllerDelegate> *delegate;
#protocol NotificationManagementViewControllerDelegate <NSObject>
#optional
- (void)didFinishSettingNotification:(NotificationManagementController *)notificationManagementController;
.m
- (void)sendMessageToDismiss {
if ([[self delegate] respondsToSelector:#selector(didFinishSettingNotification:)]) {
[self.delegate didFinishSettingNotification:self];
}
}
And finally in the delegates .m:
- (void)didFinishSettingNotification:(NotificationManagementController *)notificationManagementController
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
You are not getting a leak because you create a new controller and ARC will release this allocation for you.
But, it's better to create a #property for your new view controller.
and modify your i.e. implementation like :
#property (nonatomic, strong) ViewController *myViewController;
if (!_myViewController)
self.myViewController = [[ViewController alloc] init];
self.myViewController.delegate = self;
[self presentViewController:_myViewController animated:YES completion:nil];
Here, you have a lazy property and you don't create a new one ViewController after the first creation.
But, you need to pass your delegate (or any property) outside your test.
Furthermore, if you use your first implementation and add this controller in a subview of the current controller without property, this will work but you will get a leak.
I got this experience with the code below :
RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
ViewController *myViewController = [[ViewController alloc] init];
[self.view addSubview:myViewController.view];
}
myViewController will be add on the screen but released immediately without keeping any reference of the object, so if you add an action in 'ViewController`, your application will crash without explanation of XCode.
So, the correct way to write this without leak will be :
- (void)viewDidLoad
{
[super viewDidLoad];
if (!_myViewController)
self.myViewController = [[ViewController alloc] init];
[self.view addSubview:self.myViewController.view];
}
The answer is a bit longer and can be improved so don't hesitate !
Hope it's going to help some people.
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.