I'm having trouble understanding an Exception, which is thrown a couple of seconds after the home button is pressed.
*** Terminating app due to uncaught exception 'NSDestinationInvalidException', reason: '*** -[GMMLoader performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
I'm programing an application, which uses a webservice to load different datasets. The webservice is added to an operationQueue.
WebService* op =
[[WebService alloc] initWithSearchString:self.searchBar.text
notificationCenter:self.progressNotification];
[self.queue addOperation:op];
[op addObserver:self
forKeyPath:#"isFinished"
options:NSKeyValueObservingOptionNew
context:NULL];
[op release];
In the -(void) start message of the web service, I call this start selector to be performed in the main thread, to be able to interact with the UI.
- (void)start
{
if (![NSThread isMainThread]){
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:YES];
return;
}
...
}
All of this works fine, but when I press the home button while the web service is active, I get the mentioned exception.
My guess is, the main thread is halted after the home button was pressed (Is that so?). So the action which is currently running is no longer updated / valid.
I tried to listen to the event UIApplicationWillResignActiveNotification to react on the home button press by canceling or suspend the operations in queue. However this did not help. Am I right with my guess, that this error occurs because of the halted main thread?
How else could I manage to stop / cancel the current webservice thread?
Thank you in advance
Zerd
I found the error.
As it turns out, it was not a problem with the webservice itself, but with a geolocator, which was created and started twice.
Due to this, some kind of zombie thread was created, which would never stop.
Related
I need to schedule a task in background when the application enter background state.
I have to do this in order to call a remote service each x time and then show a local notification when some event happend with the remote service.
(I know it's looks like RPN, yes it is, but for some reason I am not able to use PRM)
I tried this code :
- (void)applicationDidEnterBackground:(UIApplication *)application{
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void){
remoteServiceCallThread = [[NSThread alloc] initWithTarget:self selector:#selector(doRemoteCall:) object:nil];
[remoteServiceCallThread start];
}];
}
- (void)applicationWillEnterForeground:(UIApplication *)application{
[remoteServiceCallThread cancel];
}
I put breakpoint in the doRemoteCall selector, put is not working.
Maybe my approach is not the best one. If you have any other hack to doing this operation like I describe it I'll take it.
Thank you.
You are not starting the thread, it's initialization code is at the expiration handler block which will be called right before the app is shut down with a timeout:
A handler to be called shortly before the application’s remaining
background time reaches 0. You should use this handler to clean up and
mark the end of the background task. Failure to end the task
explicitly will result in the termination of the application. The
handler is called synchronously on the main thread, thus blocking the
application’s suspension momentarily while the application is
notified.
The task should be active for 10 minutes only (that is driven by iOS) if your app is not supporting one of the background modes (gps, audio, voip).
You also need to keep the returned UIBackgroundTaskIdentifier reference to be able to mark it as ended if the user brings the app to foreground or when task time is going to the end (that's when the handler block is called).
My app is downloading JSON objects in when the app enters background mode. The app converts them to core data entities. The issue that I'm running in is that I need to merge the managed object contexts for these core data entities with the main managed object context.
The way to merge these changes is through notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextHasChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
- (void)contextHasChanged:(NSNotification*)notification
{
NSLog(#"received notification with object: %#",[[notification object] description]);
if ([notification object] == [AppUser managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextHasChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[AppUser managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
For some reason my code does not receive these notifications while running in background. Does the app continue to generate notifications while it is running in background mode? Or is it something with the location of where/when I register for such notifications that's wrong?
Thank you for the clarification!
app continues to send notifications either in main or background. you need to take care of
the observer should not be released during add observer and posting notification. i thnk there may some mistak in implementaion read this
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html
Once you press the Home button, your app goes into suspended mode. It won't process the above notifications until you "wake" it up by tapping on its icon.
To ensure that your app continues to do its task, you need to request background task completion. The OS will then give you up to 600 seconds (10 minutes) to complete whatever task your app is doing before suspending it.
You can read more about it here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
Specifically, look for "Background Execution and Multitasking" in the above link.
There are limitations to what type of notifications you can receive while in the background. Also the sending of notifications is something you schedule before entering the background.
If you need to continue doing work when the app enters thebackground you should call beginBackgroundTaskWithExpirationHandler as well.
Main documentation is here:
http://developer.apple.com/library/ios/ipad/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
Scroll down to the section in Background Execution and Multitasking
I hoped that the answers to squeezemylime's question would solve my problem as I also have a UIAlertView without text field but it did not work out well. Instead of a text field I placed a UIActivityIndicatorView animating on it and no buttons used on creation. So the alert gets programmatically dismissed so that the delegate method destroys the spinner on the alert and the alert itself (which has been retained through the time running).
So, I'm getting also these wait_fences: messages within the debugger and I'm frozen in the meantime because everything I tried including the valuable hints in this thread won't kill these wait_fences: messages. There is no crash, neither with GDB nor on the device.
Stepping through the code I realized that wait_fences: message is shown directly after the delegate for dismissWithClickedButtonIndex:0 animated:NO releases the questionable UIAlertView. Then I thought it could be similar to squeezemylime's delay technique and did this:
[theAlertObj performSelector:#selector(dismissWithClickedButtonIndex:animated:) withObject:nil afterDelay:0.1];
and the wait_fences: message was gone! But the call to dismissWithClickedButtonIndex:animated: can't be satisfied because it is missing the second argument animated:NO - so I tried it with some changes implementing a method for the call:
- (void)dismissNamedAlert: (UIAlertView*)alert
{
[alert dismissWithClickedButtonIndex:0 animated:NO];
}
- (void)postProcessLogicalWork: (id)arg
{
[self performSelector:#selector(dismissNamedAlert:) withObject:theAlertObj afterDelay:0.2];
...
}
Bummer: the wait_fences: message re-appeared! I think I can't go back to the working solution having an unsatisfied method call - so how can it be done? And/or under which circumstances do these wait_fences: messages occur?
Briefly the workflow for my thing is like this:
viewDidAppear calls its super and then my data updater afterDelay:0.3f
the data updater invokes a new thread for the update procedure
the new thread creates and displays the alert with spinner on the main thread and then runs for some seconds
the new thread performs a finishing procedure on the main thread and is done
the finisher dismisses the alert as described above (with wait_fences:) and refreshes the display if needed
the rest is idle time ... for the moment being
Any ideas what to do?
I don't know the reason why thing happens but sort out this problem by changing this line
[alert dismissWithClickedButtonIndex:0 animated:NO];
to this one
[alert dismissWithClickedButtonIndex:0 animated:YES];
I've created an NSOperation in the queue like so:
ImageLoadingOperation *operation = [[ImageLoadingOperation alloc] initWithImageURL:url target:self action:#selector(didFinishLoadingImageWithResult:)];
[operationQueue addOperation:operation];
[operation release];
And this works fine but if the view gets popped before the operation finishes the App crashes with "EXC_BAD_ACCESS"
I've tried to cancel the the operation Queue by calling cancelAllOperations but as its already in process it doesn't prevent the App from crashing. The docos say that if the operation is running it is up to the operation to detect that it has been canceled and respond appropriately but not too sure how I would implement this?
Any ideas?
It is a general problem for View calling some network and then callback.
My solution is you can retain the view before you call the operation. And then, when the operation finishes, you release the view.
- (void)longTask {
[self retain];
}
- (void)longTaskDidFinish {
// do something if you want
[self release];
}
You will have to either override the "cancel" operation in your ImageLoadingOperation class, or have your ImageLoadingOperation add itself as KVO observer to the "cancelled" property. There - you can intelligently cancel your operation in such way that it won't crash.
Also, if your ImageLoadingOperation runs in the background, it would be wiser to defer your access to the views somehow to the main thread (where all drawing takes place). You could use a dispatch_sync(dispatch_get_main_queue(), ^{}); or even performSelectorOnMainThread for actual access to the related view.
You see - the whole point of using an operation queue is to remove dependencies, and let things run in parallel, but your operation must synchronize with the view-system changes, and that must be designed for completeness and robustness.
You could retain the view before the callback of operation is called, as vodkhang mentioned above. But that will prolong the life of the view unnecessarily because since the view is popped you don't want the operation to continue any more.
Here is a sketch about what you should do to respond to the cancel command:
- (void)start{
if(self.isCancelled){
[self markAsFinished];
return;
}
//start your task asynchronously
}
//If you want to cancel the downloading progress immediately, implement your own 'cancel' method
- (void)cancel{
[super cancel];
if(self.isExecuting){
{
......
cancel load process
......
}
[self markAsFinished];
}
}
- (void)markAsFinished{
......
change 'finished' to YES' generate KVO notifications on this key path
change 'executing' to 'YES'; generate KVO notification on this key path
......
}
This sketch is based on ASIHTTPRequest networking library, and
there is an official guide on how you should respond to cancel command.
I have an iphone app, it run some thread to compute search. The search is made calling a time consuming function from a library.
I need to exit from the thread when the app is terminating, otherwise the thread continue to run and the search create problem when i reopen the app.
I tried to subscribe in the thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mainApplicationWillTerminate) name:#"UIApplicationWillTerminateNotification" object:nil];
And in mainApplicationWillTerminate
-(void)mainApplicationWillTerminate;
{
[NSThread exit];
}
The problem is still present, any idea?
As stated in the docs you should avoid using [NSThread exit]. In general, to avoid memory leaks and other disasters, a thread should never be stopped "from the outside". A thread should always exits by itself.
In your thread main loop you should check if the thread was cancelled:
if ([[NSthread currentThread] isCancelled]) {
return;
}
To cancel it you call its "cancel" method from another thread.
In your case you should setup an application delegate (see UIApplicationDelegate)
- (void)applicationWillTerminate:(UIApplication *)application
{
[myThread cancel];
}
Better have a look to the nice NSOperation class also.