If in my app I have non-critical data that is "global" to the app rather than specific to any particular view controller, are there any guidelines on where such data should be deallocated?
In particular: can I assume that on low memory, all view controllers will receive the didReceiveMemoryWarning event-- or at least, that the app's "initial" view controller will do-- and therefore just put the deallocation in once place? Or do I need to put it in all didReceiveMemoryWarning methods, because essentially any (but not necessarily all) of the view controllers' didReceiveMemoryWarning methods could be called?
All view controllers in the current window hierarchy will receive the automatic memory warning messages. Those not won't, but you can easily register a notification handler using UIApplicationDidReceiveMemoryWarningNotification.
That said, it sounds like you have a singleton model object that you just want deallocated when memory gets low. You can register for that notification right in the singleton class, but the more correct design pattern would be for the ViewController to own the model (and thus, it couldn't be a global singleton), then that ViewController would be responsible for telling the model when to deallocate resources.
Related
I have two view and second view is pushed from first view. For testing, I go to second view from first view and then I go back to first view. After that, I send nsnotification and in my second view, it receive my notification.
1) May I know why I receive notification in second view after it is pop up? For IBOutlets, I declare weak property also.
2) If it is still in memory, for other data like nsdictionary, nsstring, shall I use strong or weak property? Will those also in memory?
3) If I don't want my second view in memory totally, how shall I do?
If your second view controller is not released when you "go back" to the first view, then either
You are not really "going back" - you are accidentally creating a new first view controller and pushing it, which is unlikely; or:
You have a retain cycle in your second view controller.
I'm betting that you do have a retain cycle. You should try to track this down. You mention notifications: it is very easy to create an accidental extra retain when setting up a view controller as a notification observer, so that's probably the cause.
In particular, see my book's discussion of this topic:
If you called addObserverForName:object:queue:usingBlock:, you will leak (under ARC) unless take elaborate precautions (such as doing the weak-strong dance in the block, to avoid a strongly retained self).
I have singleton. My singleton has UIViewController property. When I push some view controller I set pushed view controller to the singleton property.
For example I pushed B view controller from A view controller
and inside B view controller initialization code i set the property of singleton:
inside init code:
Singleton *singleton = [Singleton sharedInstance];
singleton.viewController = self;
This code means even when I pop back to previous controller A the instance B never be destroyed as I think and seems I will have memory leak.
So each time when I will push B controller I will increase memory usage.
How can solve it. I have tried use weak instead of strong for singleton property but I am not sure that is the solution.
The expected way for me - something like cascade destroying. But maybe I confused and this code will not cause memory leak. What do you think.
Not really. You aren't leaking the instance because you still have a reference to it. And the memory usage doesn't increase because next time you push B it will set itself to the singleton and replace the previous instance (which will then be deallocated).
Generally, if you do want to store the reference, you should make it 'weak' and / or have the view controller remove itself when it is removed from its parent.
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.
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.
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.