【问题标题】:A nice way to perform a selector on the main thread with two parameters?用两个参数在主线程上执行选择器的好方法?
【发布时间】:2011-12-25 12:19:51
【问题描述】:

我正在寻找一种在主线程上使用两个参数执行选择器的好方法

我真的很喜欢使用

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

方法,除了现在我有两个参数。

所以基本上我有一个委托,我需要在加载图像时通知它:

[delegate imageWasLoaded:(UIImage *)image fromURL:(NSString *)URLString;

但是我这样做的方法可能会在后台线程中调用,并且委托将使用此图像来更新 UI,所以这需要在主线程中完成。所以我真的希望代理也能在主线程中得到通知。

所以我看到了一个选项 - 我可以创建一个字典,这样我只有一个对象,其中包含我需要传递的两个参数。

NSDictionary *imageData = [NSDictionary dictionaryWithObjectsAndKeys:image, @"image",     URLString, @"URLstring", nil];
[(NSObject *)delegate performSelectorOnMainThread:@selector(imageWasLoaded:) withObject: imageData waitUntilDone:NO];

但这种方法对我来说似乎不合适。有没有更优雅的方法来做到这一点?也许使用 NSInvocation? 提前致谢。

【问题讨论】:

    标签: objective-c multithreading selector nsinvocation ios-3.x


    【解决方案1】:

    在这种情况下,使用 NSDictionary 传递多个参数是正确的方法。

    不过,更现代的方法是使用 GCD 和块,这样您就可以直接向对象发送消息。此外,看起来您的委托方法可能正在做一些 UI 更新;您在主线程上正确处理。使用 GCD,您可以轻松地做到这一点,并且像这样异步:

    dispatch_async(dispatch_get_main_queue(), ^{
        [delegate imageWasLoaded:yourImage fromURL:yourString;
    });
    

    用这个替换你的performSelector:withObject 调用,你就不必为了改变你的方法签名而搞砸了。

    确保你:

    #import <dispatch/dispatch.h>
    

    引入 GCD 支持。

    【讨论】:

    • 非常感谢,我想到了,但我的部署操作系统是 3.0,我不想为 iOS 4.0 和 3.0 编写不同的代码。还有NSInvocation呢,这里能用吗?
    • 真的吗? 2 操作系统落后于当前操作系统?我已经更新了标签以反映这一点。但是,如果您正在启动一个新应用,并且没有非常充分的理由为 iOS3 编写代码,那么您最好为最新的 SDK 编写代码。
    • 好吧,不幸的是,这不是我的决定:(
    【解决方案2】:

    由于您无法访问 GCD,因此 NSInvocation 可能是您最好的选择。

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

    【讨论】:

    • 这是GCD之前的正确解决方案,如果要经常使用,可以轻松放入自定义方法performSelectorOnMainThread:withObject:withObject:
    • 谢谢!这就是我要找的东西!
    • 使用后是否需要释放任何东西? (由于保留了参数)
    【解决方案3】:

    也可以使用以下方法:

    - (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
    

    根据此方法的文档- 在延迟后使用默认模式在当前线程上调用接收者的方法。

    【讨论】:

    • 但问题是我希望它在主线程中发送,无论它是在哪个线程中调用的
    【解决方案4】:

    是的,您的想法是正确的:您需要将要传递给主线程上的委托的所有数据封装到一个通过performSelectorOnMainThread 传递的对象中。您可以将其作为NSDictionary 对象、NSArray 对象或一些自定义的Objective C 对象传递。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多