用两个参数在主线程上执行选择器的好方法?

Posted

技术标签:

【中文标题】用两个参数在主线程上执行选择器的好方法?【英文标题】: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? 提前致谢。

【问题讨论】:

【参考方案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 对象传递。

【讨论】:

以上是关于用两个参数在主线程上执行选择器的好方法?的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 选择器的状态和进度

[ jquery 过滤器 find(expr|obj|ele) ] 此方法用于在选择器的基础之上搜索所有与指定表达式匹配的元素,这个函数是找出正在处理的元素的后代元素的好方法

labview条件结构的分支选择器的问题

单击 ImageView 并在 Swift/Xcode 中执行没有选择器的函数

Prisma 查询解析器失败:“where 选择器的参数无效”

javascript: 一个可以代替jquery选择器的方法