How to stop a task initiated using NSOperationQueue? - iphone

I have created a process using NSOperationQueue in the below way:
queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation)object:nil];
[queue addOperation:operation];
[operation release];
In the loadDataWithOperation method I wrote code for printing infinite numbers.
Now I have called cancelAppOperations to stop its execution and also I have tried to set suspended value to YES like this
[queue setSuspended:YES]
even though the process is still running, I am calling the stoping method by using "stop"button. It is calling in the same class.
How can I stop it?

You can send a cancellation message to all operations in the queue before they begin executing, but if your operation is already executing you need to process the cancel message

Here for suspend of queue
[queue setSuspended:YES] is working fine,there I check the condition for stop the For loop i.e
if(![queue isSuspended])
{
NSLog(#"elements are :%d",i);
}
else
{
break;
}
I placed these lines of code in "loadDataWithOperation" method

Related

How perform Some task In Background in iOS

I want perform some database related task in background for that I have added code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^(void) {
[lclDB deleteRecoredwithBlock:^(BOOL success) {
if (success) {
NSLog(#"Deletion Succesful...");
}
}];
});
deleteRecord function internally calls number of methods sequentially to perform delete operation in local database.now I have wait until all delete operation is performed.but I want to do this whole delete operation in background.if any one known please help me to figure out these problem.
Any NSObject can perform action in background using the following :
[myObject performSelectorInBackground:#selector(anAction) withObject:nil];
More information on apple documentation.
Try performSelectorInBackground:withObject: method.
[self performSelectorInBackground:#selector(backgroundMethod) withObject:nil];
You can also use NSInvocationOperation.
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(deleteDataWithOperation) object:nil];
[queue addOperation:operation];
And this is your deleteDataWithOperation method -
-(void)deleteDataWithOperation
{
//Do your work here
}

How to Stop a currently executing thread

Scenario is like this--
In my app there is an Scroll View with many instances of
MyCustomImageDownloaderController(containing imageViews where images are to be assigned after downloading) .
As per our requirement, an image has to be downloaded as we move on to a page.
Scroll View + (MyCustomImageDownloaderController1, MyCustomImageDownloaderController2, MyCustomImageDownloaderController3.... etc)
Let's say i am scrolling on it,
i reached to page 1 --> image for it should start downloading
i reached to page 2 --> image for it should start downloading...so on
and if i am on page 3 and images for previous pages if not been dowloaded, they should stop downloading.
So i tried it with using threads..
on API..
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender{
Step 1) calculated currentPageNumber
Step 2) started thread for downloading image with url for this currentPage
//startBackGroundThreadForPlaceImage:(NSURL *) url
Step 3)stopped thread for previous page , if that is still running
}
Now My MyCustomImageDownloaderController is as
-(void) startBackGroundThreadForPlaceImage:(NSURL *) url{
if(isImageDownloaded == NO){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:imageUrl];
myThread = [[NSThread alloc] initWithTarget:self selector:#selector(loadImageInBackground:) object:imageUrl];
//[NSThread detachNewThreadSelector:#selector(loadImageInBackground:) toTarget:self withObject:imageUrl];
[myThread start];
NSLog(#"The current thread is %# ", [[NSThread currentThread] name]);
[pool release];
}
}
NOW Here selector does the work of loading image and assigning to image view
Now Stopping the thread
-(void) stopBackgroundThread{
[myThread cancel];
//[[NSThread currentThread] cancel];
//if([[NSThread currentThread] isCancelled]) {
//[NSThread exit];
//}
[NSThread exit];
}
-(BOOL) isThreadRunning{
return [myThread isExecuting];
}
So i tried a lot of things, but could not Stop the thread in between..
Basically once instantiated thread using any of three methods
1) perform Selector in BackGround
2) NSThread detach new thread
3) NSThread alloc..init with..
In first 2 methods how to get the instance of the newly created thread, so that i could stoop it,
as NSThread currentThread doest not give that
in Method 3,
myThread = [[NSThread alloc] initWithTarget:self selector:#selector(loadImageInBackground:) object:imageUrl];
when i tried
[myThread cancel];
It did not cancel that thread,,
When i tried
[NSThread exit];
it hangs on current screen,,,,i guess it has stopped the main thread
Please help me
Thanks in Advance*strong text*
It's generally better to ask the thread to stop, rather than forcing it, which should be considered a last resort. You should, therefore, frequently check a 'stop flag' and when this gets set, terminate the current thread (by simply exiting the method, in this case). You just need to provide a property on the class the thread is operating on so callers can ask the thread to stop.
It's no different in C++ or Java.

Child thread randomly gets killed when app comes back to the foreground

I understand that when an iOS application gets put into the background, all child threads are put on hold. My question is why would the OS kill a child thread (randomly)? Sometimes when the app comes back to the foreground, it works fine, but other times the child thread is killed.
This is how I create the child thread:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(syncTimerRunning)
object:nil];
[queue addOperation:operation];
[operation release];
* Update *
Changed the code as follows, in a hope to debug it further.
self.queue = [NSOperationQueue new];
self.operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(syncTimerRunning)
object:nil];
[self.queue addOperation:self.operation];
//[operation release];
Also checking the following in one of my NSTimer loops, to check if the thread is getting killed.
if([self.queue isSuspended]) {
NSLog(#"queue is suspended");
}
if([self.operation isCancelled]) {
NSLog(#"operation is cancelled");
}
if([self.operation isFinished]) {
NSLog(#"operation is finished");
}
I haven't been able to reproduce the problem yet, after commenting out [operation release] and making it a class property, which gets released when class does.
* Another update *
I was under the impression that when you added an operation to queue, it retained it in memory, so that the operation release wouldn't have actually been the cause. Still attempting to reproduce the problem after the change.
* And another update *
Alright, I was able to reproduce it again, and it spit out operation is finished, so [self.operation isFinished] is true. I don't understand how or why it's triggering that as finished, when it's clearly not. I have an NSLog that should be triggered right before the child thread is finished - here is the syncTimerRunning method.
- (void) syncTimerRunning
{
while (self.secondsCount > 0) {
// need to query the server and get the current running timer and update the seconds
TimeboxedEvent *te2 = [TimeboxedEvent getTimeboxedEvent:self.agentProfileId andIsPlayer:((self.gift == nil) ? YES : NO)];
long timeLeft = (self.timeboxedEvent.timeBoxedEventTotalSeconds - (([te2.timeBoxedEventCurrentTimestamp longLongValue] - [te2.timeBoxedEventBeginTimestamp longLongValue]) / 1000));
NSLog(#"retreived timebox: %# - current time: %# - time left: %ld - current seconds: %i", te2.timeBoxedEventBeginTimestamp, te2.timeBoxedEventCurrentTimestamp, timeLeft, self.secondsCount);
if (timeLeft >= 0) {
self.secondsCount = timeLeft;
} else {
self.secondsCount = 0;
}
sleep(10.0f);
}
NSLog(#"seconds count: %i", self.secondsCount);
}
What do you mean by "killed" and why do you believe it is happening? Have your verified that your application is not terminated between the time you enter the background and when you enter the foreground. If you are suspended, you will not receive a notification that you're being terminated.
In what ways can syncTimerRunning terminate? It is more likely that it is doing so (perhaps in response to an unexpected error) than that the OS is killing one thread in your application.
EDIT
What do you mean by "killed." Do you mean that you believe pthread_cancel() is called (why do you believe that?) or do you mean "my NSLog() entries no longer seem to show up?"
When you say "after the application comes back" do you mean "the thread continues to run for a while after applicationWillEnterForground:, and then I no longer see it running" or do you mean "it never appears to run again?" Does the thread still exist when you attach a debugger and check the stack? What state is the thread in?
What operations in in the queue when this happens? Does the NSOperation object go away? If not, what state is it in? Is it marked isCancelled or isFinished?
Well, I found the problem.
It was occasionally failing on this line:
TimeboxedEvent *te2 = [TimeboxedEvent getTimeboxedEvent:self.agentProfileId andIsPlayer:((self.gift == nil) ? YES : NO)];
After it failed, it automatically terminated the thread, which is why it was setting isFinished to YES.
It was failing because the phone would occasionally lose internet connectivity, which it required in order to do the sync. I just added an internet check before it called the getTimeboxedEvent and voila.
In cases like this it is good to run the program in the debugger and create a "breakpoint on exception". Probably something in your NSOperation causes an exception to be thrown. If this is the case, then the breakpoint will trigger immediately and you will have a complete call stack where the exception is thrown.

Help me to understand this code snippet

I would like to know and understand this code snippet
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(navigatePage)
object:nil];
[queue addOperation:operation];
[operation release];
[queue release];
-(void)navigatePage
//==================
{
[self performSelectorOnMainThread:#selector(loadPageDetails) withObject:nil waitUntilDone:NO];
[myTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
Thanks for your time.
In a nutshell, the code looks to be designed to do some processing in a background thread -- it is probably fetching some data over the network (loadPageDetails), and then it is updating the UI with the results (reloadData). However, loadPageDetails is being called on the main thread, which I don't understand -- surely that should be done a background thread, if it is time consuming?
Can you give a fuller context for your code? I don't really see the point of using NSInvocationOperation in the above example, because all the operation does is shove more bits of work back on the main thread.
The usual reason for using background processing would be to not block the main thread when doing something that takes time to complete -- I assume the bit of code that sets up the operation queue is called on the main thread?

How can I SIMPLY perform two tasks at once in my iPhone app? (threading?)

The Situation:
Somewhere in my app I start downloading data from my server. Before downloading starts, I would like to update a UILabel to say #"Now Downloading...". And set it back to blank when downloading is over.
The Problem: It seems like the download takes up all of the computers attention, and the UILabel never gets updated until the very end (at which downloading is already over) and so is set back to blank (or, never-visible in real time).
Question:
How can I SIMPLY update my UILabel to say "Now Downloading" just before the download?
label.text = #"Downloading";
NSOperationQueue *operationQueue = [[NSOperationQueue]alloc]init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(download) object:#"http://www.google.com"];
[operationQueue addOperation:operation];
[operation release];
- (void)download:(NSString *)url
{
// do the download
[self performSelectorOnMainThread:#selector(didFinishDownload) withObject:nil waitUntilDone:NO];
}
- (void)didFinishDownload
{
label.text = #"";
}
If you use NSURLRequest -> NSURLConnection and the NSURLConnection's delegate methods this will perform the download in the background and will notify the delegate of incoming data. This will also allow you to display a progress.