在主线程上调用一个方法?

97

首先,我正在为 iPhone 编写代码。 我需要能够在主线程上调用一个方法,而不使用 performSelectorOnMainThread。 我不想使用 performSelectorOnMainThread 的原因是,在尝试创建单元测试的模拟对象时会出问题。

[self performSelectorOnMainThread:@Selector(doSomething) withObject:nil];
问题在于我的模拟对象知道如何调用doSomething方法,但不知道如何调用performSelectorOnMainThread方法。
那么有什么解决方法吗?
6个回答

301

Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
    [self doSomething];
});

Swift

DispatchQueue.main.async {
    self.doSomething()
}

旧版 Swift

dispatch_async(dispatch_get_main_queue()) {
    self.doSomething()
}

你用 Swift 3 写的代码让我今天过得很愉快。谢谢! - Felipe Balduino Cassar
最好的做法是不要直接在块中使用self,而是使用它的弱引用。 - Jageen

2

在软件开发中有一句话,即添加一个间接层几乎可以修复任何问题。

将doSomething方法作为一个间接壳,只执行performSelectorOnMainThread调用really_doSomething方法来执行实际的"Something"工作。或者,如果您不想更改doSomething方法,请让模拟测试单元调用doSomething_redirect_shell方法来完成类似的操作。


1

现在在Swift 3中:

DispatchQueue.main.async{
   self.doSomething()
}

1

这里有一个在Swift中更好的方法:

runThisInMainThread { () -> Void in
    // Run your code
    self.doSomething()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

这个功能已经作为我的仓库的标准函数包含在内了,看看这里:https://github.com/goktugyil/EZSwiftExtensions


你现在使用的方法名可能会误导人,听起来像是代码块会立即在主线程上执行,但事实并非如此。在主队列上使用dispatch_async将代码块添加到下一个runloop中,这个重要的行为被隐藏在被称为“runThisInMainThread”的方法后面。 - aryaxt
我还没有找到一种方法来配置Xcode以遵循简化语法 :( - aryaxt
我尝试了一下,你是正确的。它不仅将代码添加到下一个运行循环中,还延迟了其后的内容。为什么会发生这种情况? - Esqarrouth
1
这是预期的行为,dispatch_async将代码添加到队列的末尾。如果您希望立即调用它,则应改用dispatch_sync。如果在已经存在的线程队列上执行dispatch_sync,则会导致线程锁定。在您的示例中,打印顺序为"a","c","b"。因为a和c在同一作用域中执行,所以它们在1个runloop中执行。b被添加到队列的末尾,因此当队列中的其他现有项完成时,它将在稍后某个时间被调用。 - aryaxt
1
@Esqarrouth - 你确定你的dispatch_async在调用后会阻塞代码吗?使用async而不是sync的整个目的就是不要阻塞后面的内容。(当然,代码块将会阻塞主线程上的任何其他东西,因为所要求的代码的目的是在主线程上执行。如果您想运行后台代码,则应请求不同的队列,而不是dispatch_get_main_queue。) - ToolmakerSteve
显示剩余3条评论

0

现代 Swift 的解决方案是使用 actor 实现安全的多线程,即 MainActor

@MainActor func setImage(thumbnailName: String) {
    myImageView.image = UIImage(image: thumbnailName)
}

我在这里概述了更多基于Actor的主线程解决方案。


-4
// Draw Line
    func drawPath(from polyStr: String){
        DispatchQueue.main.async {
            let path = GMSPath(fromEncodedPath: polyStr)
            let polyline = GMSPolyline(path: path)
            polyline.strokeWidth = 3.0
            polyline.strokeColor = #colorLiteral(red: 0.05098039216, green: 0.5764705882, blue: 0.2784313725, alpha: 1)
            polyline.map = self.mapVu // Google MapView
        }

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接