objective-cmultithreadingselectornsinvocationios-3.x

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.


Solution

  • 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];