在我们完成执行dispatchworkItem并尝试使用dispatchqueue.main.sasync更新UI之后,



给定以下代码

@IBAction func buttonClick(_ sender: Any) {
    recordLabel.text = "Perform some time consuming networking..."
    let workItem = DispatchWorkItem {
        // Perform some time consuming networking...
        DispatchQueue.main.async {
            // QUESTION: Is it possible, that the UI recordLabel is 
            // destroy/ invalid at this stage?
            self.recordLabel.text = "Done"
        }
    }
    DispatchQueue.global().async(execute: workItem)
}

  1. 在用户线程中执行耗时的工作(例如网络(
  2. 完成耗时工作后,在UI线程中更新UI

我想知道,在UI更新期间,是否有机会,UI是销毁的,或者不再有效?

原因是,对于Android生态系统,它们倾向于在用户线程执行中"重新创建" UI(请参阅最佳实践:在方向更改期间的异步(。这使用户线程保持UI参考不再有效。

我想知道,iOS Swift是否表现出类似的行为?

我想知道,在UI更新期间,是否有机会,UI是销毁的,或者不再有效?

如果所讨论的视图控制器已被驳回,则视图将从视图层次结构中删除。但是,您写这篇文章的方式,如果视图控制器已被驳回,则您的代码将更新一个不再可见的视图。更糟糕的是,直到此派遣块完成之前,与您的视图控制器及其视图相关的内存及其视图都不会被划分。这样做是没有意义的。

所以,如果我们要使用您的代码模式,则可以这样做:

@IBAction func buttonClick(_ sender: Any) {
    recordLabel.text = "Perform some time consuming networking..."
    let workItem = DispatchWorkItem { // use `[weak self] in` pattern here, too, if you reference `self` anywhere
        // Perform some time consuming networking...
        DispatchQueue.main.async { [weak self] in
            // Update the view if it’s still around, but don’t if not
            self?.recordLabel.text = "Done"
        }
    }
    DispatchQueue.global().async(execute: workItem)
}

或更自然地,

@IBAction func buttonClick(_ sender: Any) {
    recordLabel.text = "Perform some time consuming networking..."
    DispatchQueue.global().async { // use `[weak self] in` pattern here, too, if you reference `self` anywhere
        // Perform some time consuming networking...
        DispatchQueue.main.async { [weak self] in
            // Update the view if it’s still around, but don’t if not
            self?.recordLabel.text = "Done"
        }
    }
}

值得注意的是,人们通常不会将网络请求发送到全局队列,因为URLSession,Alamofire等网络库已经不同步执行了他们的请求。因此,您不会将async派往全局队列。

同样,如果此网络请求仅是为了更新此视图控制器视图的内容,则在删除视图时,您甚至可以取消网络请求。(为什么要继续进行网络请求只是为了更新可能不再存在的视图呢?(这取决于请求的性质。

最后,当您在后面发现这个即时问题时,您可能会重新考虑视图控制器是否应该发出网络请求,而不是其他类型的对象。这远远超出了这个问题的范围,但从长远来看,要重新考虑的东西。

但是,我们无法在此观察中进一步评论这些观察结果,而无需看到您在此派遣到全局队列中所做的事情。

ui在您的视图控制器DealLocs之前,不会被划分。当您创建DispatchWorkItem的闭合并派遣主队列时,您将保持内部自我。因此,它将增加视图控制器的保留计数。因此,直到您的保留视图计数无法获取0视图控制器将不会被视为视图(iboutlet(。

现在,如果您在闭合中添加[unowned self],并且在执行块之前,视图控制器因您解散或从导航堆栈中的弹出而进行处理,您会发现self.recordLabel.上的崩溃,因为自我没有保留在块内。对于[weak self],它可能正常工作,直到您不强制解开自身为止。

在闭合中引用 self引起了对该实例的有力引用,这意味着那样长由于参考的存在,对象将还活着。只要捕获其活着的封闭,参考就会存在。

如果未指定另有指定,例如通过[weak self][unowned self],默认情况下强烈关闭任何参考。

所以您应该没事,在内存方面。不过要当心,强烈捕获self可以导致保留周期。您的代码目前没有这个问题,但是您永远不知道代码将来如何演变。

另一个问题是为什么您要长时间保持控制器的活力?可能会发生在网络调用完成时,控制器不再在屏幕上,因此UI操作徒劳无功。

在旁注上,您可以直接捕获recordLabel,因为您只需要在派遣中需要:

let workItem = DispatchWorkItem { [recordLabel]
    // Perform some time consuming networking...
    DispatchQueue.main.async {
       recordLabel.text = "Done"
    }
}

这将避免保留周期,但是它不能确保控制器将保持活力,它只能确保在MAIN派发发生时仍然存在标签。但是同样,除非控制器除了更新UI以外还需要一些额外的业务逻辑工作,否该recordLabel直到其ViewController被销毁之前,不会从内存中释放出来。

但是,如果您的ViewController在网络完成之前可能会破坏UI时,则应使用[weak self]

从理论上讲,您的应用程序可能在此操作过程中被系统暂停或终止,或者用户从该视图中导航,有可能处理拥有recordLabel(self(的对象。我想说的是,在这种情况下,将自我视为弱者是一个很好的防御练习。

DispatchQueue.main.async { [weak self] in
        self?.recordLabel.text = "Done"
    }

通常,要维护性更好,必须具有一个对异步调用的对象,从您想要完成的工作来定义工作。有替代的API/模式可以进行调度,当您回到代码时,如果需要,更改和更新将更容易。

最新更新