I am trying to perform this action on the main thread:
[curItem.mButton setBackgroundImage:newArt forState:UIControlStateNormal];
So I do this...
cWrapperObject* obj = [cWrapperObject alloc];
[obj setupParams :curItem.mButton :newArt];
[obj performSelectorOnMainThread:#selector(setImageForButton) withObject:nil waitUntilDone:YES];
I feel like this is bad, does anyone has any suggestions on how I could approach this differently?
Another option is GCD. You can invoke a block on the main queue which gets run serially when the run loop runs. blocks aren't limited to one object like performSelectorOnMainThread.
dispatch_async(dispatch_get_main_queue(), ^{
// code here
});
I wrote a more comprehensive comparison of performSelectorXXX and GCD here complete with samples:
GCD, Threads, Program Flow and UI Updating
Also, here's another related SO post:
GCD to perform task in main thread
If you need to pass only one parameter, you should set up "withObject:" argument in method performSelectorOnMainThread:withObject:waitUntilDone. So your method should be declared as
-(void)setImageForButton:(id)parameter
and you should invoke method on main thread with:
[obj performSelectorOnMainThread:#selector(setImageForButton:) withObject:newArt waitUntilDone:YES];
Note ':' in #selector(setImageForButton:) this means that this method will be messaged with one argument, passed in withObject:
Related
can any one tell me what is different when I call method using performSelectorOnMainThread and calling same method without performSelector.
For Exa.
-(void)sampleCALL{
..........
}
NOW Call this method using these two senario:
[[self performSelectorOnMainThread:#selector(sampleCALL) withObject:nil waitUntilDone:NO];];
or
[self sampleCALL];
How these two method are getting executed?
Please help me to find this concept properly.
in firs one case [self sampleCALL]; your method will be called in the thread where control was at current time. it will maintain all the stack manipulation what it does for method calling from another method.
while
[[self performSelectorOnMainThread:#selector(sampleCALL) withObject:nil waitUntilDone:NO];];
calls the method in main thread whatever the controls current thread is. All UI actions are performed in main thread always.
I am working on someone else's code. I came across a line of code
[NSThread detachNewThreadSelector:#selector(myMethod) toTarget:self withObject:nil];
I have 2 questions to ask.
Its just calling a method. Why is NSThread used here?
While running the code, On some instances, this method doesn't get called. When I put a breakpoint inside the method, it always get called. But if I remove the breakpoint, on some instances the method doesn't get called. Is this the problem of NSThread?
Using NSThread in this way means that the method "myMethod" is being called on a background thread, concurrently with the rest of the code. It is equivalent to this, which you may also have seen:
[self performSelectorInBackground:#selector(myMethod) withObject:nil];
If the method is not getting called (or seeming to not get called), it may be down to concurrency issues, i.e. the fact that the execution order of that method and ones you call after in on the main thread is not guaranteed, so you are expecting it to be called earlier than it actually is.
If you say:
[NSThread detachNewThreadSelector:#selector(methodA) toTarget:self withObject:nil];
[self methodB];
Then methodA and methodB will be running at the same time and there is no guarantee that methodA will finish before methodB.
I always use NSThread detachNewThreadSelector in combination with an auto-release pool, like so:
-(void)myMethod {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
// .. Do stuff in this thread
[pool release];
};
If you want to "simply" perform a selector, do it like this:
[self performSelector:#selector(myMethod)];
In the scenario in which i have a thread launched, can i still acces methods on the parent thread? Is there a specific way to call this methods? If so, what is it?
Note: in my scenario both thread are for data manipulation, they are not interface-related threads ( i know this was to be considered in .NET, don't know it they are in Objective-c).
In this case, it is best to use Grand Central Dispatch (GCD) instead of working with NSThead or NSOperation directly.
Overview of Concurrency: http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091
Intro to Grand Central Dispatch: http://cocoasamurai.blogspot.com/2009/09/guide-to-blocks-grand-central-dispatch.html
With your example, you can use nested calls into Grand Central Dispatch to achieve this functionality:
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.exampleQueue", 0);
dispatch_async(backgroundQueue, ^{
// operate on data in the background here
NSData *stuff = [self doSomethingComplex];
dispatch_async(dispatch_get_main_queue(), ^{
// Perform Task back in the main thread
[viewController updateStuff:stuff];
});
});
This method is the preferred method for performing these kind of tasks. In addition, by utilizing blocks, it is also very easy to understand the code at a glance without having to example multiple methods within your class.
Threads by definition share the state of parent thread. In ObjectiveC, if you spawn a worker thread & want to call some method on main thread, this can be done like so-
[self performSelectorOnMainThread:#selector(someMethod:) withObject:nil waitUntilDone:NO];
If they are not interface stuff, or can result in some interface stuff you can call then you can just call then, and do any of the usual thread safety stuff you have to do in any language, like #syschronise(obj) or NSLock. But if it is stuff that will result in interface stuff then you will have to do as 'Srikar' wrote [self performSelectorOnMainThread:#selector(setDataCount:) withObject:count waitUntilDone:NO]; which will effectively place the message onto the NSRunLoop cue.
i need to pass variables to the thread method when creating a new thread
my code is the follwing
//generating thread
[NSThread
detachNewThreadSelector:#selector(startThread)
toTarget:self withObject:nil];
thread job
- (void)startThread:(NSInteger *)var img:(UIImageView *) Img{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread sleepForTimeInterval:var];
[self performSelectorOnMainThread:#selector(threadMethod) withObject:nil waitUntilDone:NO];
//i need to pass Img to threadMethod:
[pool release];
}
thread Method
- (void)threadMethod:(UIImageView *) Img {
//do some coding.
}
so how i can do this (pass parameter to both of methods
The code you provided as I see it is only using the thread to implement a delay. You can do this easily without introducing a thread like this:
[myImageView performSelector:#selector(setImage:)
withObject:image
afterDelay:5.0];
For more complex needs I have written a category on NSInvocation that allow you to easily call any method, independent of the arguments, on any thread.
You have for example this method as I see it:
-(void)doStuffWithImage:(UIImage*)image callbackAfterDelay:(NSTimeInterval)to {
NSAutoreleasePool* pool = [[UIAutoreleasePool alloc] init];
// ... do stuff
[NSThread sleepForTimeInterval:ti];
[self performSelectorOnMainThread:#selector(callbackWithImage:)
withObject:image
waitUntilDone:NO];
[pool release];
}
This is easy enough, but spawning this method on a secondary thread is not that easy. My category allow you to do it with this simple code:
[[NSInvocation invocationWithTarget:self
selector:#selector(doStuffWithImage:callbackAfterDelay:)
retainArguments:YES, image, 5.0] invokeInBackground];
This is where you can find the code and a blog post elaborating on why and how it was implemented:
http://blog.jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/
You can pass only one argument by using withObject:, change your code as follows
[self performSelectorOnMainThread:#selector(threadMethod)
withObject:image
waitUntilDone:NO];
If you need to pass more than one value make it as a array, and then pass it.
And UIComponents are not thread safe, so be careful while passing UIcomponents to threads.
I'm reasonably certain UIImage isn't thread safe, so you may be out of luck there. In general though, any of these:
Make the object an instance variable
Make the object a global
Capture the variable in a block and use dispatch_async to do your threaded work instead of NSThread
Send the object to the thread using NSConnection
etc...
Remember though, just because you have a reference to the object doesn't mean it's safe to use. Consider thread-safety guarantees (main thread only vs one thread only vs one writer only vs thread safe), and consider where you need to use locks or queues to guard resources shared between threads.
here is what i want:
create an object that 'lives' in its own thread, all the methods should be executed in that thread.
i.e:
// i'm in the main thread
MyClass *myObject = [ [MyClass alloc] init ]; // it creates its own thread
[myObject method1]; // should execute the method1 in myObject's thread
[myObject method2]; // should execute the method2 in myObject's thread
[myobject release]; // should deallocate everything that is used for myObject and remove myObject's thread
i have been reading about threads and runloops. I created a new thread on the init method, its entry point is the runloop method. The runloopMethod just set the most basic stuff needed for running a NSRunLoop and runs it.
aThread = [[NSThread alloc] initWithTarget:self selector:#selector(runloopMethod) object:nil];
[aThread start];
it worked fine, but when i call a method ( i.e: [myObject method1];) from the main thread it runs it on the main thread, how do i know it?, well, because method1 performs several operations that blocks the UI. What i have done is to redirect the call in this way:
// on MyClass.m
-(void) method1 {
if ([NSThread currentThread] != aThread) {
[self performSelector:#selector(method1) onThread:aThread withObject:nil waitUntilDone:YES];
}else {
// do my stuff
}
it's working, but this way limits me, also i have some questions for you:
i have realized that if i'm in X-thread and call a method of some object, it will be executed in X-thread. I think that the method call will be added (not sure if it's the word) to the X-thread's runloop. right?
Is there a way to set that: any call to my object's methods will be executed on the object's thread? (without doing all this stuff).
also, is it the correct way for what am i doing?
method1, method2, and so on are the sync version of my functions..so they will block the UI. that' why i assume having another thread is the way.
thanks for reading!.
btw. i'm not using GCD since i need to support iOS 3
The Objective C method dispatch runtime code has no mechanism (AFAIK) to determine implicitly whether to do a generic method call on a different thread than the current one, so you will have to implement your own explicit background call mechanism, as you did, using performSelector.
If you set waitUntilDone to YES on your call to your background thread from the main thread, you will still block the UI.
If you want your method1 to run in the background and not block the UI, set waitUntilDone to NO, and have to background thread inform the main thread about completion (or anything else) using performSelectorOnMainThread.
You might alternatively be able to use operation queues to send messages to your background thread's run loop.
I'm guessing you are trying to use threads to run background tasks in order to keep the UI responsive. That's good, but this would be a very difficult approach. Try this instead:
1) From the main thread, fire off a new thread:
[NSThread detachNewThreadSelector:#selector(methodThatTheThreadWillRun)
toTarget:nil
withObject:nil];
2) Write methodThatTheThreadShouldRun and do whatever you need to do in it. It will be executed in the thread you just created. When it finishes, have it call a threadIsFinished on the main thread:
- (void)methodThatTheThreadWillRun {
MyClass *myObject = [ [MyClass alloc] init ];
[myObject method1];
[myObject method2];
[myobject release];
[self performSelectorOnMainThread:#selector(threadIsFinished)];
}
3) Finally, write threadIsFinished:
- (void)threadIsFinished {
// do whatever you need to do here: stop a spinner, etc.
// this will be invoked by the background thread but will
// execute on the main thread
}