我想要一种简单的方法,将代码放在方法的开头,强制该方法仅在主线程上运行(因为该方法更新UI元素)。
目前,我有这样的东西:
if (![NSThread isMainThread]){
[self performSelectorOnMainThread:_cmd withObject:_results waitUntilDone:NO];
return;
}
但我想要一种方法将其包含在宏中,而无需输入该方法的参数。似乎应该有某种方法来迭代传递给当前方法的参数列表并创建 NSInvocation 或类似内容。有什么想法吗?
这行得通吗?
#define dispatch_main($block) (dispatch_get_current_queue() == dispatch_get_main_queue() ? $block() : dispatch_sync(dispatch_get_main_queue(), $block))
如果您也从主线程调用它,这也将起作用,这是一个奖励。如果需要异步调用,只需使用 dispatch_async
而不是 dispatch_sync
即可。
尝试在不同的线程上重新调用您的方法,我建议使用 dispatch_sync()
和 dispatch_get_main_queue()
来确保只有敏感代码在主线程上。这可以很容易地包装在一个函数中,就像Brad Larson对"GCD在主线程中执行任务"的回答一样。
他的过程本质上与您已有的过程相同,区别在于代码被放入一个块中,并根据需要调用或排队:
if ([NSThread isMainThread])
{
blockContainingUICode();
}
else
{
dispatch_sync(dispatch_get_main_queue(), blockContainingUICode);
}
如果您愿意,也可以毫无困难地将其转换为宏。
创建块本身不需要太多更改。如果您的 UI 代码如下所示:
[[self textLabel] setText:name];
[[self detailTextLabel] setText:formattedDollarValue];
[[self imageView] setImage:thumbnail];
将其放在要排队的块中,如下所示:
dispatch_block_t blockContainingUICode = ^{
[[self textLabel] setText:mainText];
[[self detailTextLabel] setText:detailText];
[[self imageView] setImage:thumbnail];
};
如果使用 NSThread 条件三元从主线程或后台线程调用,这将起作用。
同步:
#define dispatch_main(__block) ([NSThread isMainThread] ? __block() : dispatch_sync(dispatch_get_main_queue(), __block))
异步:
#define dispatch_main(__block) ([NSThread isMainThread] ? __block() : dispatch_async(dispatch_get_main_queue(), __block))
要使用:
-(void)method {
dispatch_main(^{
//contents of method here.
});
}
归因:灵感来自理查德·罗斯的回答
这样的方法创建动态 NSInvocation 的唯一方法需要您的方法的参数是va_list。
您需要能够将当前方法的参数作为数组获取(以便您可以循环数组并将参数添加到 NSInvocation),我不确定这是否可能(我认为不是)。
我认为这就是您要查找的:
- (void)someMethod
{
// Make sure the code will run on main thread
if (! [NSThread isMainThread])
{
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:YES];
return;
}
// Do some work on the main thread
}