Apple's documentation says "When a segue is triggered, it causes a new view controller to be instantiated and transitioned onscreen." Some behavior I'm seeing has me questioning if the VC is actually destroyed and recreated each time the view is seque'd to.
In my application each time I seque to a particular view I note that the the following viewController function is called (which is consistent with the VC being recreated each time it is sequed to):
- (id)initWithCoder:(NSCoder *)aDecoder
Note, this viewController is also a datasource for it's collectionView and has a property:
#property (strong, nonatomic) NSMutableArray *messages;
And every time initWithCoder is called I alloc and init a messages array and assign it to the above pointer.
When I navigate back to another view and then seque back again I'm seeing strangeness that has me thinking that the VC might be being re-used. Or alternately if the first instance wasn't destroyed and can still receive notifications.
The first thing that is strange is that in viewDidLoad if I call addObserver:self for a particular notification which I want this VC to handle then the second time I seque into this VC I will get two notifications sent to the VC's notification handler for every notification sent. This is consistent with the VC being reused otherwise why would the handler get called twice?
But going against this re-use scenario is that initWithCoder is in-fact being called the second time the View is loaded.
Since I don't want 2 or more notifications generated for every notification post, I use a static boolean to guarantee that addObserver is only called once. I.e. addObserver is only called the first time viewDidLoad is called, and not subsequent times.
viewDidLoad and notification handler access the messages array. On the second seque, viewDidLoad seems to access a newly alloc/init'd messages array but when the notification handler accesses messages it seems to be the previous messages array from the previous time the VC was loaded.
Any ideas?
Make sure you are unregistering yourself as an observer on those specific keys once the view is being dismissed. You might have old method still being performed because they were never unregistered.
Related
Say if I have a navigation stack like this: UINavigationController->ViewController1->ViewController2. When ViewController2 gets popped, does the system guarantee the executing order of below methods?
dealloc of ViewController2
viewWillAppear of ViewController1
I've tested for a while and only see dealloc is called first, but I don't know if this can be guaranteed, i.e., is it also possible ViewController1's viewWillAppear gets called before ViewController2's dealloc?
There is no such guarantee of order, and it's even likely viewWillAppear of your ViewController1 is called before ViewController2.dealloc is called.
The reason is that depending on autorelease pools and internal references, it's likely the system still has a reference to the view controller that is just disappearing/has just disappeared when it calls the viewWillAppear of the view controller that's about to get shown.
If you require cleanup to be run when ViewController2 is popped, implement viewWillDisappear/viewDidDisappear. Again, there is no guarantee that vc2.viewWillDisappear will be called before vc1.viewWillAppear, but you can rely on vc2.viewWillDisappear getting called before vc1.viewDidAppear (note Did, not Will in the last one).
I have an Mvvmcross app that has a TableViewController in root. Each row opens a DetailViewController. And inside each one of it, you can edit it in a EditViewController.
So I have a ViewModel for each view. I'm dealing with two problems here:
1 ) In DetailViewController i subscribe it to a database message. When i close it, i gotta dispose this subscribeToken. So i would need to call this when DetailViewController got destroyed. But cant call it when it disappears, because when I open editViewController it will send a message that DetailViewController gotta listen to.
So I cannot dispose it in ViewDidDisappear method. But the other option would be in ViewDidUnload. But this method is only called in MemoryWarnings. So it is not disposing the token. That is not good .
2) The other problem is: for each DetailsViewcontroller that i open, i have to save in Settings what is the current id. and then when I leave, i have to remove it from Settings. So the same problem here. If i remove it in ViewDidDisappear it will remove when i'm in EditViewController, and i cant, it gotta be set there. But then if i remove only in ViewDidUnload it will not be called, and this variable must be removed.
When should I call the OnDestroy method to both cases?
In Android i'm calling in OnDestroy. Where should i call it in iOS?
Thanks in regards,
ViewDidUnload is not an option - it's deprecated and won't be called (since a long time ago - e.g. maybe since iOS5?).
iOS doesn't really provide a general ViewController override for when the ViewController is "no longer used". However, if you have control of the ViewControllers in your app - e.g. if you are using a NavigationController which never reuses ViewControllers after they have been popped - then it should be relatively straight-forward to provide your own "cleanup" method and to call it from your own navigation control logic - e.g. from a custom presenter using events generated by a NavigationController.
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.
Hi I have a viewcontroller in a tabview controller. I decided to use NSNotification to flag when views in the tabview controller need to update their data. Previously each had logic to tred to decided the state of the data model and update accordingly.
My update code calls some stuff that in turn call delegate methods. These were all working when not using notification.
My first attempt at notifying seemed to call the selector before the view controller had initialised (before viewDidload at least). Among other thing the delegate methods were never called when the update in the notification selector was run and the view controller didn't get updated. Seems like the viewcontroller is in some unknown state.
I ran a test and put the same update code in the viewDidLoad and only called the notification code after the viewDidLoad had been called. This works.
My question is ,is there another way of preventing the notification selector method being called before viewDidLoad or the object is otherwise correctly initialised.
I am using storyboard so I am not programming the creation of views etc.
I hope this is clear - posting a whole bunch of code would not had been any easier.
Thanks guys, both right! I set up the Notification in app delegate. I should put it at the end of viewDidLoad. Then it is only called when there is a subsequent update and I don't need a flag to stop the first notification from doing an update before the object is up and running.
I have a viewController called "FirstViewController". In an IBAction i call another ViewController called "thePageFlipViewController" and push it in sight via
[self presentModalViewController:thePageFlipViewController animated:YES];
after some time the user closes thePageFlipViewController with a button where the following code is executed via a delegate in FirstViewController:
[self dismissModalViewControllerAnimated:YES];
[thePageFlipViewController release];
And here is my problem:
-viewDidLoadin FirstViewController get's sometimes called after dismissing thePageFlipController. I don't understand why, because firstViewController should live in background. Is it dependent how long the modal view is displayed? is it possible that ARC does release something?
My problem is, that i initialise a lot of objects in viewDidLoad and the app crashes if viewDidLoad gets called again. I define some Routes for RESTKit there and RestKit complains that the routes are already set up and crash the app.
Any Help is appreciated.
When a view is not actually displayed it can be unloaded to free up memory. You would get a call to viewDidUnload: when that happens so you can release any objects you are holding strong references to. Then next time the view is needed, viewDidLoad: will get called again when the view is reloaded, there you have to recreate the objects you released in viewDidUnload:.
See the Memory Management section of the UIViewController class reference.
Also this answer has a good explanation already.