Message sent to deallocated instance ARC - ios

I am doing an application which downloads image from server and displays it on a view. I am using delegate for that. Once the image is finished loading a delegate sends message to the view to show the image.
The above scenario is working fine. But if I move out from that particular view to any other view, when the image loading is finished the delegate tries to send message and causes an error.
I tried setting the
imageFetcher.delegate=nil;
in view didUnload. Also before calling the delegate method in download class I check for delegate is nil.
But i can see that the delegate object is not nil.
if(delegate!=nil)
{
[delegate imagefetcherView:self didLoadImage:image];
}
How can I fix this error?
Thanks

Do not rely viewDidUnload to do any cleanup. That's only called in iOS versions prior to iOS 6, and only when the view is unloaded due to memory pressure (but not when you just dismiss/pop the view).
Set your delegate to nil in the dealloc method or viewDidDisappear or wherever is appropriate.
Two caveats relevant to picking which method you'll nil the delegate:
Be aware that viewWillDisappear and viewDidDisappear will also be called if you push/present another view controller, even if the current one has not been yet been dismissed. Only rely upon these disappear-related methods if the view controller in question does not ever push/present another view controller.
If employing the dealloc technique, note that this only works if the delegate is a weak property of the image fetcher class (and delegates generally should be weak). If the delegate was a strong or retain property, that will prevent the view controller's dealloc from ever getting called.
By the way, I gather that you are letting the image fetch continue, even though the view controller has been dismissed. You might want to not only nil the delegate, but cancel the request, too. It depends upon whether (a) you're using a fetch that even permits a cancellation (e.g. a NSURLConnectionDataDelegate approach or a AFNetworking operation) and, if so, (b) whether you want it to cancel or not. It's easy, though, to tie up precious network resources (esp if on a slow cellular connection) letting requests continue even if the user doesn't need it anymore. It depends upon the particulars of your app.
Regardless, do not rely upon viewDidUnload.

viewDidUnload isnt called in iOS 6+.
you should use this
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
imageFetcher.delegate=nil;
}

You still can have a dealloc call in your class but it should not call [super dealloc]. If you add it you can set up a breakpoint here and see from where its gets its retain count to 0. Or use Instruments to track retain/release cycle of your controller.

I would implement a separate cache to temporarily store the picture in care view controller is deallocated but the picture can be used once again, e.g. if the user gets back to the same page.
In that case you would have a long-lived cache object as a delegate. View controllers can, for example, subscribe to receive key-value notifications about incoming pictures when those view controllers become visible (just don't forget to unsubscribe from KVO in viewWillDisappear).
If your controller is invisible but likely to be shown again you'll have the picture in cache (to be dropped if low memory); of course you can also check in the cache if your picture is never likely to be shown again and drop the picture.

Related

Is dealloc always called? Even when you swipe close an app?

I wonder if dealloc is always called when you close a ViewController in Objective-C for iOS. I've done some tests and it seems like it. Except I don't get any logs when I swipe close the application. I figured that maybe XCode doesn't log things if you fully close the app like that.
The reason I wonder this is because I'm sending analytic data when one of the ViewControllers closes and I kinda need to know if dealloc is always called or if there's any better way doing this.
If you want to get notified when app is killed via Swiping , you can use applicationWillTerminate method and in this method , you can check for current view controller in navigation stack and send analytical data for that View controller .
It's not defined when dealloc will be called for a view controller (or pretty much any class), therefore don't use it for sending analytics.
Use viewDidDisappear instead, which you can rely on to be called when the view controller has been removed.

UIViewController stay active in background

I use the following to open a new view controller
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController pushViewController:mainMenu animated:NO];
When the mainview is active on the screen the application still runs old view controller (methods) in the background.
How i can stop the old view controller from running or make it inactive?
First, let's ask ourselves this: what kind of methods run on a view controller?
I'm going to categorize the methods that are part of a UIViewController subclass into 4 categories (though there's clearly overlap among these categories).
Life-cycle methods. These are methods such as viewDidLoad, viewWillAppear:, etc. that run as part of the view controller's life-cycle. None of these methods should be running if the view is "in the background" other than, arguably, viewDidLoad, viewWillAppear: which are technically called before the view has come to the foreground, and viewDidDisappear: which is technically called just after the view has left the foreground. So we don't really need to worry about these.
Delegate methods. Your view controller could delegate objects. The delegate methods are called on your view controller whenever the object they're delegating calls them. In the majority of the cases, we're talking about delegating some sort of UI element which is part of the view controller. This UI element would be "in the background" any time the view controller is, so it shouldn't be calling delegate methods.
NSTimer called methods. NSTimer can keep a strong reference to an object, and keep that object alive until the fire time ticks over and it calls the specified method on that object. These methods are certainly likely to be called even if the view controller is in the background, but if you want to stop the timer from running if the view controller's view isn't visible, then just invalidate it in viewDidDisappear:.
Miscellaneous methods. These methods don't necessarily into any of the above categories (though they could). Most of the time these methods are called are from within the view controller themselves, so if nothing is being called from one of the first three categories, it will be rare that this category is called (unless the method is public).
So you've got methods running "in the background" on your view controller. What category do they fall in?
If they're in the first category, then either you're calling a life-cycle method manually from somewhere (which I can't recommend against enough--there's no good reason to do this), or perhaps you don't understand the UIViewController life cycle.
If they're in the second category, then it's most likely good that they're being called. If the Object A is being delegated by View Controller A and Object A is still working on some process and still requires delegation, then it's extraordinarily important that View Controller A is still responding to the delegation methods. If you don't want this to happen, you need to stop Object A from requiring delegation.
If they're in the third category, invalidate your NSTimer objects when it's appropriate. NSTimer doesn't just magically not do what you scheduled it to do. You have to invalidate it in viewDidDisappear if you don't want it to call methods on the view controller when its view is not visible.
If they're in the fourth category, start by making sure you've not got public methods that you're calling manually from outside the view controller. If you do, well stop calling them. If you don't, go double check the first three categories.
If you mean that the view controller that was active before is still executing code, I'd suggest doing the following to remove it from view and execution before pushing:
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController popViewControllerAnimated: NO]; // Takes the old one away
[self.navigationController pushViewController:mainMenu animated:NO];

UIViewController - View events life cycle and registering for KVO / Notifications

I'm wondering if there's any way -viewWillAppear: would be called without a matching -viewDidAppear:. Ditto for -viewWillDisappear and -viewDidDisappear.
The root of my question is where to register and unregister for KVO and / or NSNotifications of an object who's change notifications will cause the view controller to update views.
For example, I have a model object that is being processed asynchronously and it's string properties could change. I'd like the view controller to KVO those properties and have any changes reflected by swapping out the text of a label managed by said view controller.
Where do you register and unregister for notifications and why?
EDIT:
A gotcha I've come across is what to do in cases of application state change (e.g. -applicationWillResignActive, -...didEnterBackground, etc). These changes don't seem to trigger view controller lifecycle methods. Any best practices here?
With the standard container view controllers, you will always get will/did messages in pairs. If you have written your own view controller container, you may not, but that would be a bug in the container implementation.
Most of the time, you'll want to set things up and tear things down in the 'will' messages. This gives your view controller the earliest shot at anything it needs to do before it becomes "active" and also shuts down things as early as possible when you no longer need them.
When pushing a view controller in nav stack, it is entirely possible that a notification will occur during the push animation. If you set up the observers in viewDidAppear, you would miss that notification. You want to be listening as soon as possible.
Likewise, I would reckon that viewDidDisappear is too late to remove callbacks. For example, a location manager could be stopped in viewDidDisappear, but another location update could be delivered during the disappearing animation. That probably doesn't hurt much, but depending on the application, something weird could happen like an alert view appearing after you've already left a view controller - which looks like a flicker to the user.
Anything non-view related, as indicated above, occur in the 'will' methods. The choice about will vs did, then, is really about what the user sees. Animations should start in viewDidAppear, otherwise, the user won't see the frames that occur during will/did appear. Data should be moved to views in viewWillAppear, otherwise, a blank view will transition in and the data will only appear after the transition animation completes. Also, it possible that a view frames could be adjusted in between viewWillAppear/viewDidAppear, like in the case of a previous view controller in the stack hiding/showing the navigation bar.
And on a side note, not something I will get into great detail here with, but I'd advocate against KVO for controller interactions that move data from model to view objects. Difficult to test and difficult to trace.
You can subclass your UILabel and in your subclass override the setText method:
-(void)setText:(NSString *)newText {
//do your KVO updates here
[super setText:newText];
}
hope this helps

NSNotification addObserver/removeObserver proper methods to place them in

I've seen NSNotification addObserver/removeObserver placed in viewDidLoad/viewDidUnload, viewDidAppear/viewDidDisappear,dealloc`....
What are the proper methods to use here so things are neat and tidy?
That depends. Do you only want to receive notifications when your view controller is on screen (then viewWillAppear/Disappear is probably a good choice) or also when the view is not currently active. In the latter case, the init method might be even better suited than viewDidLoad (or does it matter for the notification that the view is currently in memory?).
Also note that viewDidUnload is not called in all cases (only when the view gets unloaded but the view controller remains in memory – if the view controller is deallocated while the view is loaded, only dealloc is called and is the correct place to unregister.
For viewControllers:
I would say in viewWillAppear: and viewDidDisappear:.
The reason is that you care for these notifications as long as your view is "on screen".
Since a view does not need to be unloaded when your view is offscreen viewDidLoad and viewDidUnload are the wrong locations for (de)registering notifications.
For registering for notification for non views:
In the designated initializer and dealloc.

how to free memory when logout app?

i add a login view above on my app NavigationController,when login success,the login view hidden.and when press a logout button,the view will show.during logout,i wanna free the navicontroller's memory, the navicontroller has much view and data model,when logout it,wanna free all just leave the navi.
has any method to do it? thx.
You can use your UIViewController's (doc) viewDidDisappearand viewWillAppear callbacks to get rid of or recreate some of your view and data. But memory management in iOS will surely do housekeeping for you and call your controllers viewDidUnload method on low-memory conditions (memory warnings). So I suggest your implement your clean-up within the viewDidUnload method and care for view and data setup in viewDidLoad. See the comment of viewDidUnloadfor more Info:
When a low-memory condition occurs and the current view controller’s views are not needed, the system may opt to remove those views from memory. This method is called after the view controller’s view has been released and is your chance to perform any final cleanup. If your view controller stores references to the view or its subviews, you should use this method to release those references (if you retained the objects initially) and set those references to nil. You can also use this method to release any objects that you created to support the view but that are no longer needed now that the view is gone. You should not use this method to release user data or any other information that cannot be easily recreated.

Resources