4

我想要一种简单的方法在方法的开头删除代码,这将强制该方法仅在主线程上运行(因为该方法会更新 UI 元素)。

目前,我有类似的东西:

 if (![NSThread isMainThread]){
    [self performSelectorOnMainThread:_cmd withObject:_results waitUntilDone:NO];
    return;
}

但我想要一种将其包含在宏中的方法,而无需输入该方法的参数。似乎应该有某种方法来迭代传递给当前方法的参数列表并创建一个 NSInvocation 或类似的。有什么想法吗?

4

5 回答 5

5

这行得通吗?

#define dispatch_main($block) (dispatch_get_current_queue() == dispatch_get_main_queue() ? $block() : dispatch_sync(dispatch_get_main_queue(), $block))

如果您也从主线程调用它,这也将起作用,这是一个奖励。如果您需要异步调用,只需使用dispatch_async而不是dispatch_sync.

于 2012-06-13T00:19:16.243 回答
3

我建议不要尝试在不同的线程上重新调用您的方法,而是使用dispatch_sync()dispatch_get_main_queue()确保只有敏感代码位于主线程上。这可以很容易地包装在一个函数中,就像Brad Larson 对“GCD to perform task in main thread”的回答一样。

他的过程与您已经拥有的过程基本相同,不同之处在于代码被放入一个块中并根据需要调用或入队:

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];
};
于 2012-06-13T00:16:23.300 回答
2

如果使用 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.
    });
}

归属:灵感来自理查德·罗斯的回答

于 2018-07-31T00:10:51.640 回答
1

我知道从这样的方法创建动态 NSInvocation 的唯一方法需要方法的参数是 va_list。

您需要能够将当前方法的参数作为数组获取(以便您可以循环数组并将参数添加到 NSInvocation),我不确定这是可能的(我不认为它是)。

于 2012-06-13T00:04:54.597 回答
0

我认为这就是你要找的:

- (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
}
于 2013-12-04T14:10:55.023 回答