I have created a customise table cell in which i am downloading image in background thread in cell class and now suppose i moved to previous view controller. Would background thread still would execute and will crash the application. I could;t test this scenario as images loading fast and didn't get chance to move to previous view controller.
What would be the consequences?
Problems can certainly arise if you don't do this correctly. You probably won't get a crash, because the NSURLSession prevents that for you by retaining its delegate. But by the same token, the view controller you have left will leak; it won't go out of existence, and the downloads and other things will continue to happen.
If you are doing it correctly, all downloads should be cancelled by you when the view controller goes out of existence. If you don't need these downloads occupying bandwidth and CPU time, you have a duty to call invalidateAndCancel to make that happen. Note that you generally cannot do this in the view controller's deinit, exactly because of the retain cycle. But once you have done it, your view controller will be able to go out of existence in good order.
Related
I am new in iOS and I have tab bar. I have 4 tabs like Facebook.
In my case all data should be updated from server anytime when user goes one of that screen.
One guy said calling viewDidLoad frequently can make memory leaks. Is that true?
So what's the best play here? Every time call viewDidLoad() and load data from server or there is another way to handle this not calling viewDidLoad() every time.
There is no pull to refresh in that screens
viewDidLoad() will only be called the first time the view controller is loaded. Using a tab bar controller will usually keep the view controllers in memory as the user switches tabs so if you want the loading to occur every time the user goes to a new screen, this is not the best place for it.
I would suggest using viewWillAppear or viewDidAppear. If you're updating data from the network, make sure to do the loading on a background thread to ensure the interface does not get blocked (regardless of which method you use).
Personally I would put network loading code inside viewDidAppear as to me it makes more sense to call the network after the view has finished appearing since it will presumably not finish immediately. This way it is also easier to present a UI element that shows data is loading to the user.
Tapping a button in ViewController1 segues the app to ViewController2.
Tapping the button also triggers a slightly CPU-intensive operation. (i.e., merging several images and writing the result to disk). This operation cannot get initiated until the button is tapped.
The side-effect is a poor user experience. The ViewController1 button stays highlighted a beat longer, and the segue is noticeably delayed.
Moving the intensive code to ViewController2's viewDidLoad function seems unhelpful since it will produce a delay, but on ViewController2's side not ViewController1.
Moving the code to ViewController2's viewDidAppear also seems undesirable since this operation should only get fired once, not every time ViewController2 appears. Adding a variable just to track whether the operation has fired seems suboptimal.
Another option is to put the code in ViewController2's viewDidLoad, but introduce a slight delay with dispatch_after, ensuring the code doesn't compromise the segue.
What's the right approach to handle this pattern?
Is there another UIViewController function that should get used for this purpose (i.e., code to execute only once, and only after the UIViewController has presented itself to the user)?
At a high level, here's a thought:
Make a singleton class that has your "result" as a property. When view controller 2 asks for it, the singleton can create it (the result) if it doesn't already exist (if it DOES exist, well, you're already done).
Push that long running job into an operation queue and let the operation send a delegate message (or notification, whatever floats your boat) back to view controller 2 when it's done. When VC2 gets that message, it can "refresh".
Depending on how long that job takes, you may want to put in a spinner and/or loading message on VC2 and then get rid of it during the "refresh".
(I mentioned operation queue, mostly because I like them. It can be whatever mechanism you like, the point being that the work is not done on the main thread.)
I am building application that uses tab bar.
I have say A & B tab.
When I click on tab A, then it takes time for loading data from server.
Similarly, when I click on tab B, it takes time loading.
If click on tab A and wait until it loads all data, and then move to tab B, it works fine.
If I click on tab A and move to tab B while A was still loading data from server, it crashes the app.
I tried to unload the view at view did unload method. But, it did not work.
Can anyone suggest me how to solve this problem?
in each view loaded by tab bar controller, I added self.view = nil; for each view will disappear method. This solved by problem. May be this will be helpful.
Without seeing your code, it's difficult to answer. There could be many possible causes. However, my first guess would be that you're dispatching your NSURLConnection completion handler to the main queue and trying to update the UI, which is no longer in sync.
In other words, you start on tab A and the download commences asynchronously. You then switch to tab B in your UI, starting the second download on another queue. Your tab A download completes, and the completion handler is then dispatched to the main queue. In that completion handler, you may have code to, for example, reload a UITableView on tab A. But since you are no longer on tab A, that view controller (and all of its associated views) do not currently exist in your program's life cycle. If you're not properly checking for nil responses, etc., this could create a crash.
Another less likely possibility is you are running out of memory. Easy to test for this: simply monitor the memory usage as you navigate through the app. Your app may crash if it doesn't properly respond to a memory warning. This would be possible if you were using an NSURLCache, perhaps, and committing too much memory to both download requests simultaneously.
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.
There's a crash in the app that I'm working on that I'm having a difficult time tracking down the cause of. Here is the only set of events which causes the crash:
The app opens with the main view controller (VCmain) being presented. A button is triggered which opens a modal view controller (VCmodalA) via a segue. VCmodalA simply displays some information gathered about a core data object. VCmodalA is then dismissed by a button press. At some future point another view controller is presented modally (VCmodalB), which has some fields for the creation of a new core data object. After the object is created, if VCmodalB is dismissed, the app crashes with the following error:
*** -[UILabel _supportsContentDimensionVariables]: message sent to deallocated instance 0x818e200
If VCmodalA is not displayed prior to VCmodalB, or a new core data object is not created, or VCmodalB is not dismissed, then no crash occurs. I am at a loss what '_supportsContentDimensionVariables' means or who is sending it, although it appears to be sent to a label which was on VCmodalB (therefore the crash must be caused because a call is being made to a label that was deallocated when the view controller was dismissed).
I've spent hours poking around in Instruments looking at the Zombie left behind and trying to isolate the offending code by commenting it out, but I have been totally unsuccessful. At this point any hints would be welcome!!
Thanks so much!
There are two top causes of these kinds of errors: failure to use ARC, and direct access of ivars (particularly if you're not using ARC). Fixing those two issues is the best way to avoid these kinds of crashes.
As for how to debug it, first, you want to audit your accesses to UILabel objects. If you have any ivars that point to UILabel they should be strong or weak, never assign.
You should make sure that view controllers don't run code when they're not onscreen. This crash makes me think that this is a likely problem. For example, do not setup timers in viewDidLoad or initWithFrame:. Set them up in viewDidAppear: and tear them down in viewWillDisappear:. Similarly for KVO or delegation. View controllers manage views; if they're doing something when their view is not onscreen, your design is incorrect.