iOS performSelectorOnMainThread with multiple arguments - objective-c

I would like to perform a selector on the main thread from another thread, but the selector has multiple arguments, similar to this:
-(void) doSomethingWith:(int) a b:(float)b c:(float)c d:(float)d e:(float)e {
//...
}
How can I get this working with performSelectorOnMainThread: withObject: waitUntilDone:?
EDIT
I would like to explain why i need this.
I'm working with UIImageViews on the main thread, and I make the calculations for them on another thread. I use a lot of calculations so if i make everything on the main thread, the app lags. I know that UI elements can only be manipulated on the main thread, this is why i would like it to work this way, so the main thread can listen to touch events without lags.

When you're using iOS >= 4, you'd do this instead:
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething:1 b:2 c:3 d:4 e:5];
});
That's like doing waitUntilDone:NO. If you want to wait until the method is finished, use dispatch_sync instead.

You'll need to use a NSInvocation
Create the object, set the target, selector and arguments.
Then, use
[ invocationObject performSelectorOnMainThread: #selector( invoke ) withObject: nil, waitUntilDone: NO ];

you can pass one object of NSDictionary/NSArray type having required arguments.
and accept the same type of object in your function.
then, decompose the values and proceed with processing.
you have to use NSNumber for numeric values for adding them to NSarray/NSDictionary and later on in your function, you can convert them back with intValue/floatValue etc
best of buck.

Related

How to invoke a method with multiple arguments in background thread in Objective-C?

When I tap a button it will invoke an action which in-turn will initiate a process(through another method) and return. I thought of using
[<target> performSeleltorInBackgroundThread:....];
-but I could not pass multiple arguments through this.
How can I achieve it.
You can do it like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self goDoSomethingLongAndInvolved:arg1 and:arg2 and:arg3 and:argN];
dispatch_async(dispatch_get_main_queue(), ^{
[textField setStringValue:#"Done doing something long and involved - Update UI"];
});
});
You can not do that, but you could add arguments into the array and pass this array as the only one argument into -performSelectorInBackgroundThread. With literals you could do that pretty easy:
NSArray *arguement = #[first, second, third];
in -performSelector:
id first = argument[0];
...

ios stop 2 threads from using a method at the same time

We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.

call a function using thread in xcode

i have created a thread in xcode and i have given the function name to be called from that thread.
but my problem is that the function name which is given to call is not being called(came to know when put a breakpoint in that function)
code:
NSThread* myThread;
[myThread start];
[self performSelector:#selector(func1:) onThread:myThread withObject:nil waitUntilDone:false]
and later i tried this one also:
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:#selector(func1:)object:nil];
[myThread start];
above func1 is the name of the function to be called.
so can any one please tell me how to create the thread and call func1 from there....
In your first code sample it doesn't look like you are actually creating a new thread. You create an empty myThread variable and then call start on it but this will just result in start being sent to nil. The empty thread variable is then sent to the performSelector:onThread:withObject:waitUntilDone: method which will presumably do nothing.
You will need to properly create a thread before you can actually run something on it using performSelector:onThread:withObject:waitUntilDone:.
Alternatively, it would be much easier, assuming you don't care which background thread the method runs on, to simply use performSelectorInBackground:withObject:. For example:
[self performSelectorInBackground:#selector(func1:) withObject:nil];
Try the following if it works:
[NSThread detachNewThreadSelector:#selector(func1) toTarget:self withObject:nil];
Since you are not passing any object to your "func1" (aka: your method doesn't have parameters) you don't need to put the ":" after its name.
If your func1 accepting one argument. Then definitely it has to work with second approach which you used. May be your fuc1 has no formal argument and still u calling in selector like this #selector(fuc1:) and passing object as a nil. so may be due to this reason it is not working. It can be one reason. just try it if not.

A nice way to perform a selector on the main thread with two parameters?

I'm searching for a nice way to perform a selector on the main thread with two parameters
I really like using
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
method, except now I have two parameters.
So basically I have a delegate which I need to notify when the image is loaded:
[delegate imageWasLoaded:(UIImage *)image fromURL:(NSString *)URLString;
But the method where I do this might be invoked in the background thread, and the delegate will use this image to update the UI, so this needs to be done in the main thread. So I really want the delegate to be notified in the main thread as well.
So I see one option - I can create a dictionary, this way I have only one object, which contains two parameters I need to pass.
NSDictionary *imageData = [NSDictionary dictionaryWithObjectsAndKeys:image, #"image", URLString, #"URLstring", nil];
[(NSObject *)delegate performSelectorOnMainThread:#selector(imageWasLoaded:) withObject: imageData waitUntilDone:NO];
But this approach does not seem right to me. Is there more elegant way to do this? Perhaps using NSInvocation?
Thanks in advance.
Using an NSDictionary to pass multiple parameters is the right way to go about it in this case.
However, a more modern method is to use GCD and blocks, this way you can send messages to an object directly. Also, it looks as if your delegate method might be doing something UI updates; which you are correctly handling on the main thread. With GCD you can do this easily, and asynchronously like this:
dispatch_async(dispatch_get_main_queue(), ^{
[delegate imageWasLoaded:yourImage fromURL:yourString;
});
Replace your performSelector:withObject call with this, and you won't have to mess around with changing your method signatures.
Make sure you:
#import <dispatch/dispatch.h>
to bring in GCD support.
Since you don't have access to GCD, NSInvocation is probably your best choice here.
NSMethodSignature *sig = [delegate methodSignatureForSelector:selector];
NSInvocation *invoke = [NSInvocation invocationWithMethodSignature:sig];
[invoke setTarget:delegate]; // argument 0
[invoke setSelector:selector]; // argument 1
[invoke setArgument:&arg1 atIndex:2]; // arguments must be stored in variables
[invoke setArgument:&arg2 atIndex:3];
[invoke retainArguments];
/* since you're sending this object to another thread, you'll need to tell it
to retain the arguments you're passing along inside it (unless you pass
waitUntilDone:YES) since this thread's autorelease pool will likely reap them
before the main thread invokes the block */
[invoke performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:NO];
Following method can also be used:
- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
As per the docs of this method-
Invokes a method of the receiver on the current thread using the default mode after a delay.
Yes, you've got the right idea: you need to encapsulate all the data you want to pass to the delegate on the main thread into one single object which gets passed along via performSelectorOnMainThread. You can pass it along as a NSDictionary object, or a NSArray object, or some custom Objective C object.

Run an anonymous block on a specific background thread

At first glance it seemed like an easy question, but I just can't figure how to run an anonymous block on a certain background thread i.e. I am looking for the blocks equivalent of -performSelector:onThread:withObject:waitUntilDone:.
Related: Is it possible to associate a dispatch queue with a certain background thread, just like the main queue is associated with the application's main thread?
Edit Clarified that I am looking to run an anonymous block
I saw this function RunOnThread() recently in Mike Ash's PLBlocksPlayground (zip file, see BlocksAdditions.m):
void RunOnThread(NSThread *thread, BOOL wait, BasicBlock block)
{
[[[block copy] autorelease] performSelector: #selector(my_callBlock) onThread: thread withObject: nil waitUntilDone: wait];
}
This is what I was looking for.
There are a bunch of other very useful blocks related utilities in PLBlocksPlayground, most of which Mr. Ash explains in this post.
If I understand you right you should do this:
dispatch_queue_t thread = dispatch_queue_create("your dispatch name", NULL);
dispatch_async(analyze, ^{
//code of your anonymous block
});
dispatch_release(thread);
You also can write some method, which will take block to it, but you should know what type of parameters will it holds:
-(void)performBlock:(void (^)(SomeType par1, SomeType par2))block ToData:(Sometype)data;
You can call it with anonymous block:
[something performBlock:^(SomeType par1, SomeType par2){
//do your stuff
} ToData: data]
And in method you can call your block as a simple C function:
block(par1, par2);
A block is a function. Call it like you would call any other function.