How to tell iPhone OS function to stop/cancel? - iphone

I have an action which begins when the user taps a button on the screen (e.g. "Import"). At the same time, a UIToolbar appears on the bottom of the screen which gives the user the option to cancel this action. How can I properly send a cancel message to the initial function? If the user hits "Cancel," I do not want the "Import" to continue. In theory I could set it up as a separate thread (which I could then kill), but I am not sure what is the proper way to do this so that it could clean up after itself. What are some other strategies to be able to "kill" a function which the user has already begun?

Create a separate operation using something like:
NSOperationQueue* queue = [[[NSOperationQueue alloc] init] autorelease];
[queue addOperation: [[[NSInvocationOperation alloc] initWithTarget: self
selector: #selector(_backgroundWorker)
object: nil] autorelease]];
This way _backgroundWorker will be executed without stopping main UI thread.
One Cancel button is pressed, set some internal variable and check its value inside _backgroundWorker.

Related

Back button seems disabled iOS

In iOs I navigate (pushed) with a navigation controller, and in the viewDidLoad launch a request. This request take might take a long time, and I'd like the users will be able to go back if they won't want wait for the request.
The problem it's that the back button added in the navigation bar seems to be blocked until de request finishes. The button remember the user interaction and when the request finishes go back automatically.
The request has a delegate method for the response and when the app enters on that method the button wake up and go back.
If I touch during a request the touch/click effect don't appear on the button, and it don't either at the end of the request. When I wait until the end, the button had the normal effect for a touched/clicked button.
For that you can enable the button property like this.
[button setEnable:Yes];
[button setEnable:Yes];
also use this
[button setuserintractionEnable:no];
call your request in backgroundthread as
[self performSelectorInBackground:#selector(startRequest) withObject:nil];
and remember this always for core data too. this is that your UI will be rleased and the request will continue to work in background thread
and then implement request cancel on back button
initialize obj as
Obj *obj = [[Obj alloc] initWithDelegate:self selector:#selector(requestFinished)];
[self performSelectorInBackground:#selector(startRequest:) withObject:obj];
obj has the attributes SEL selector, and id delegate;
when request finishes
check
if ([obj.delegate respondsToSelector:obj.selctor])
{
[obj.delegat performSelector:obj.selector];
}
inform me if you need more specs

Using UIAlertView while waiting for calculations/processing data

I've set up my iphone application in a tab layout, and I would like to perform some rather intense calculations (can take several seconds to get a result) when the user selects one of the tabs.
Originally, it would appear the iphone would just hang on the original tab while doing the number crunching.
I tried adding an UIAlertView as some eye-candy, but I'm getting a fade to grey for a few seconds, then after the computations are done, a quick appearance/disappearance of the View. What I want to see is the UIAlertView appear/animate when the user touches the tab, and then disappear once the calculations are done
- (void)viewDidAppear:(BOOL)animated
{
UIAlertView *baseAlert = [[[UIAlertView alloc] initWithTitle:#"Calculating" message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil]autorelease];
[baseAlert show];
UIActivityIndicatorView *aiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
aiv.center = CGPointMake(baseAlert.bounds.size.width /2.0f, baseAlert.bounds.size.height - 40.0f);
[aiv startAnimating];
[baseAlert addSubview:aiv];
[aiv release];
/*** calculation and display routines***/
[baseAlert dismissWithClickedButtonIndex:0 animated:YES];
}
I've already seen this post, but I can't seem to figure out how apply it to my case.
The easiest way to solve this is with blocks; First schedule calculations to separate thread using first block and when done dismiss alert view via block dispatched on main thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// Do calculations
dispatch_async(dispatch_get_main_queue(), ^
{
[baseAlert dismissWithClickedButtonIndex:0 animated:YES];
});
});
You need to understand how the event loop works. When you call [baseAlert show], the alert view is added to the view hierarchy, but it isn't actually drawn to the screen until the current code block ends and control returns to the event loop. By doing your computation immediately after asking the alert view to show, you are preventing the alert from ever appearing.
It's kind of like writing a letter telling somebody you plan to paint your house, spending a week painting your house, then writing another letter saying you've done it, and THEN taking both letters and dropping them in the mailbox to be delivered at the same time.
If you have an expensive computation, the easiest way to handle it in iOS 4 and later is to place a block of code in a dispatch queue, so the work will be done in a background thread, and the main thread can still update the screen and respond to finger taps.
[baseAlert show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// we're on a secondary thread, do expensive computation here
// when we're done, we schedule another block to run on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// this code is back on the main thread, where it's safe to mess with the GUI
[baseAlert dismissWithClickedButtonIndex:0 animated:YES];
});
});
What is probably happening is that the "intense calculations" you're doing are being run in the same thread as where you're calling the UIAlertView. Setting a delegate for UIAlertView would set that up in a separate thread so that you don't need to worry about contention and whether the UIAlertView will show up before the calculations.
Alternatively, using a UIAlertView is a rather heavy handed approach - perhaps you could use some other interface element to indicate progress instead of rendering the app useless while you crunch some numbers?

How to solve UIThread bloack issue in iPhone

I am creating an iPhone application where I need to show a login screen for few minutes, hence I created the custom view and added to the custom view controller which is added to the window for display. Now at the same time I need to check for some background database so, I am creating that in separate delegate and while after database operation is in finished it gives an callback to the main thread to display the new screen. But the first view is never getting displayed and my application directly lands up in the new view.
Please find below my code snippet:
(void)CheckForExistingData : (DatabaseSource *)theDatabaseConnection
{
BOOL isRecordExist = theDatabaseConnection.isrecordExist;
// Release the connection....
[theDatabaseConnection release];
theDatabaseConnection = nil;
if (isRecordExist == FALSE)
{
textLabel.text = #"Preparing the application for first time use, please wait....";
[activityIndicator startAnimating];
[self setNeedsDisplay];
}
else
{
// Now all categories are successfully downloaded, launch the category screen...
sleep(2); // sleep for 1 second to allow to show the splash screen....
[self.viewController LaunchCategoryViewController:self];
}
}
Here CheckForExistingData is an callback mechanism which will be called from the other thread.
You need to exit your method to see anything displayed. Not sleep or wait on a synchronous network call.
That probably means you need to break your sequential code into multiple methods, the subsequent parts being called by a splash wait timer, the view button handler, or the async network activity completion callback.
sleep() blocks your main thread, thus the UI has no chance to update.
But you can always send messages delayed. In your case, it would look like this:
[self.viewController performSelector:#selector(LaunchCategoryViewController:) withObject:self afterDelay:2.0];

Issues with NSOperationQueue and dealloc being called and crashing App

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.

Stop NSThread while downloading

I have an iPhone app, where I'm displaying a tableview, that's loaded from an RSS feed. When the view is loaded, I call this method to run in a new NSThread:
- (void)start:(NSURL*)url {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLParser *XMLParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
[XMLParser setDelegate:self];
if (items) {
[items release];
}
items = [[NSMutableArray alloc] init];
[self startParsing:XMLParser];
[pool drain];
}
It's working fine, but if the user leaves the view while it's downloading or parsing the xml, I want the thread to stop running, but how would I stop it from running without leaking memory? Also, if it's running the -initWithContentsOfURL: method while I want it to stop, how would I stop that method?
If you anticipate needing to control connections (i.e. stopping a connection if the user cancels or navigates away) you should probably use the asynchronous NSURLConnection API to load your data before parsing the XML. In addition to giving you the ability to close connections as needed, you'll also be able to better respond to network errors.
As NSD pointed out, you should probably implement some sort of cancel method on the class that's driving your XML parsing thread - then just use performSelector:onThread:withObject:waitUntilDone: (or similar) from your main thread when the user cancels the download or navigates away.
These are your thread stopping options
http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSThread_Class/Reference/Reference.html#//apple_ref/doc/uid/20000311-DontLinkElementID_12
And from elsewhere in the guide
"If you anticipate the need to terminate a thread in the middle of an operation, you should design your threads from the outset to respond to a cancel or exit message."
Perhaps you should look into the NSOperation and NSOperationQueue classes.
These classes give you a massive amount of control over concurrency and asynchronous execution.
The basic idea is to create a queue, and then subclass NSOperation. Inside your subclasses' main method, do the guts of your work, in this case, you could put your start method inside here.
Then you can control the operation easily, being able to set how many operations can run concurrently, set up any dependencies some operations may have on others. You can also easily cancel operations, which is what you want to do here.
Check out the documentation for NSOperation and NSOperationQueue.