I'm running a selector on the background thread then I need to call the main thread and it works perfectly, but when there are a lot of selectors running on the background and they try to call a selector on the main thread sometimes it gets called, sometimes it doesn't. I can see the code is getting there because I'm printing with NSLog();
This is how I call the selector on the background:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(getPath:)
object:datos];
[queue addOperation:operation];
[operation release];
And this is how the background method calls the main thread method:
NSLog(#"Arrives here");
[self performSelectorOnMainThread:#selector(setPath:) withObject:array waitUntilDone:YES];
Why does it sometimes work, and sometimes it doesn't?
How do you know it doesn't work?
Is there anything blocking your main thread? A modal session, per chance?
Note that "a lot of selectors running on the background" sounds scary; un-throttled concurrency is pretty much guaranteed to be the wrong answer. It is quite easy to create a system that storms the main event loop with so much noise that it looks like events are being dropped.
Related
I have just successfully implemented skpsmtpmessage into my iPhone app. This works fine, but it runs on the main thread, causing the UI to lock up until the operation is complete. Therefore I have tried moving it to a second thread:
[NSThread detachNewThreadSelector:#selector(launchJobWithJob:) toTarget:self withObject:jobDescription];
If I do this that way, the class seems to get stuck on the connecting right away, with the only NSLog output being:
C: Attempting to connect to server at: mail.example.com:25
If I launch the job just by going [self launchJobWithJob:jobDescription];, it works fine but as I said before, lags heavily.
How can I get this to work in a background thread? Has someone come across this?
Edit: I have tried NSOperationQueue as well - the same happens again, just the log output and nothing else!
NSOperationQueue*queue = [NSOperationQueue new];
NSInvocationOperation*operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(launchJobWithJob:) object:jobDescription];
[queue addOperation:operation];
[operation release];
I'm sure that it does its network connection stuff on the runloop, so let the runloop of the thread run until the operation is finished.
[[NSRunLoop currentRunLoop] run];
Must have some race condition, you can put some NSLog at you launchJobWithJob method to detect which code cause problem.
Basically, i have a method that takes a few seconds to complete as it copies some files using NSFileManager. This is invoked on the touchesMoved event when the user picks up a draggable UIView icon. However, there's a slight delay before the icon's position is updated. I'm guessing it's waiting for that method to copy it's files before continuing. The method HAS to be triggered on touchesMoved, so please don't suggest moving it.
How can i execute a method that takes about a second to complete, without holding up the code?
(..and don't worry the copy method doesn't get repeatedly called from the touchesMoved event)
You could perform the task in the background using performSelectorInBackground:...:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
This prevent that selector from blocking the main thread.
Example:
[self performSelectorInBackground:#selector(myMethod) withObject:nil];
Do it in a background thread. Leave the main thread to deal with UI stuff only.
Technically you could divide the copying of files into very small chunks, and tell the current NSRunLoop to dispatch between each file copy.
But practically just say no to any IO access on the main thread, all IO access should be done in the background. Even the slightest block on the main thread will make the UI stutter and be unresponsive, Android user might accept that, iOS user do not.
Your options are numerous, and easy to implement. You could do a simple performSelector–:
-(void)backgroundWorker {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
}
-(void)startDoingIOStuff {
[self performSelectorInBackground:#selector(backgroundWorker)
withObject:nil];
}
You could do it practically inline using a block and GCD:
-(void)startDoingIOStuff {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL),
^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
});
}
Or you could use an NSOperation on a NSOperationQueue. I have written a longer blog post on this topic, including source code that is available here: http://blog.jayway.com/2010/08/19/future-cocoa-operation/
Before immediately resorting to a secondary thread, it would certainly be worth a try to use a plain old performSelector on self. For example:
[self peformSelector:#selector(copyFiles) withObject:nil afterDelay:0.0];
Note that this is different from doing:
[self copyFiles];
The peformSelector version basically says "do copyFiles ASAP, OK?", but doesn't block everything while waiting for it to be done. In other words, it's possible that the perform selector version would allow the main event loop to update the UI (thereby preventing the apparent visual lag) before the file copying is actually done.
I have used NSOperationQueue in my iPhone app before in iPhone OS 3.0, but now in iOS 4.0 the code is not working properly. It runs properly only once and on all subsequent calls, it doesnt work. Have there been changes in NSOperationQueue in iOS 4.0?
The relevant code is as follows:
- (void) starteffectFunction {
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(starteffectProcessing)
object:nil];
[queue addOperation:operation];
[operation release];
[queue release];
[spinner startAnimating];
}
-(void) starteffectProcessing{
some code executes. code snippet. A
......
this code is note supposed to execute before A completes. But this executes before A.
}
You are creating an NSOperationQueue, adding an operation to it, then releasing the queue. This is not how NSOperationQueues were designed to work. An NSOperationQueue is supposed to persist, with you adding operations to it as necessary.
This is probably failing because you are deallocating the NSOperationQueue before it has a chance to fire off a thread for your operation. Perhaps on the older OS versions it was just able to do this due to some timing quirk.
I recommend allocating the effect processing queue when you first need it, or in the initialization of your controller object, then keeping that queue around as an instance variable of your controller object. This queue would be deallocated at the same time as your controller object, but you will probably want to cancel all current operations at that time and use NSOperationQueue's –waitUntilAllOperationsAreFinished method to make sure that you are completing all work before deallocation.
I m using NSoperationQueue for load song from server.
This code execute when click on diff-diff buttons.
NSOperationQueue *queue;
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self selector:#selector(loadImage)object:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[queue addOperation:operation];
Problem is that when user click ie.Rama.aac then loading continue in this duration if he click krishna.aac then this process also goes into that queue.and conflicting is there.
user is finally requesting for krishna but in result first download rama.aac then krishna.aac.
I m using [queue cancelAllOperations] but it not works.
How i solve this?
Implement your own NSOperation subclass. NSOperation has isCancelled property set when cancelAllOperations is called on the queue.
If I'm not mistaken using NSOperations to load data from a network is not advised. You should use the asynchronous loading NSURLConnection provides.
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.