避免在 DispatchQueue 中使用 self

kon*_*nya 3 swift dispatch-queue

我想知道如何消除self. DispatchQueue作为一个好的实践,我们应该self只在init()

func loadAllClasses() {
    DispatchQueue.global(qos: .background).async {
      self.classVM.fetchAllClasses(id: id, completion: { (classes, error) in
        DispatchQueue.main.async {
          if error != nil {
            self.showAlert(message: "try again", title: "Error")
          }
          if let classes = classes {
            self.classesList = classes
            self.classesCollectionView.reloadData()
          }
        }
      })
    }
  }
Run Code Online (Sandbox Code Playgroud)

vad*_*ian 14

不用担心!DispatchQueue闭包不会导致循环引用。


Rob*_*Rob 6

你问:

\n
\n

我想知道如何消除self内部使用DispatchQueue

\n
\n

在Swift 5.3 中采用的SE-0269中,他们引入了一种模式,如果您将其包含self在捕获列表中,则无需使用所有self闭包中的所有引用:

\n
func loadAllClasses() {\n    DispatchQueue.global(qos: .background).async { [self] in  // note [self] capture list\n        classVM.fetchAllClasses(id: id) { (classes, error) in\n            DispatchQueue.main.async {\n                if error != nil {\n                    showAlert(message: "try again", title: "Error")\n                }\n                if let classes = classes {\n                    classesList = classes\n                    classesCollectionView.reloadData()\n                }\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

作为旁白,fetchAllClasses似乎已经是异步的,因此没有必要分派到全局队列:

\n
func loadAllClasses() {\n    classVM.fetchAllClasses(id: id) { [self] (classes, error) in  // note [self] capture list\n        DispatchQueue.main.async {\n            if error != nil {\n                showAlert(message: "try again", title: "Error")\n            }\n            if let classes = classes {\n                classesList = classes\n                classesCollectionView.reloadData()\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

作为一个好的实践,我们应该self只在init()?中使用。

\n
\n

不,您可以self在消除歧义或需要明确潜在的强引用循环的地方使用它。不要回避使用self参考文献。

\n

但消除不必要的self引用的直觉是好的,因为在不需要的地方,它最终会成为语法噪音。self如果您的更广泛的代码库洒满了内容,那么在闭包内要求引用的全部意义就会被破坏self

\n
\n

顺便说一句,上面我说明了,在你愿意捕获的地方self,你可以使用[self] in语法使 one\xe2\x80\x99s 代码更加简洁,消除很多不必要的self闭包内大量不必要的引用。

\n

话虽如此,在这种情况下我们通常希望使用[weak self]引用。当然,正如 Vadian 所建议的那样,可能不存在任何强大的参考周期风险。但这是所讨论对象的所需生命周期的问题。例如,让我们假设一下fetchAllClasses可能会非常慢。让我们进一步假设用户可能想要关闭有问题的视图控制器。

\n

在这种情况下,您是否真的希望为了完成而保持视图控制器处于活动状态fetchAllClasses,其唯一目的是更新已被解雇的集合视图?!?可能不会。

\n

[weak self]我们中的许多人在调用可能缓慢的异步进程时会使用:

\n
func loadAllClasses() {\n    classVM.fetchAllClasses(id: id) { [weak self] (classes, error) in  // note `[weak self]` so we don\'t keep strong reference to VC longer than we need\n        DispatchQueue.main.async {\n            guard let self = self else { return }                      // if view controller has been dismissed, no further action is needed\n\n            guard error == nil, let classes = classes else {           // handle error and unwrap classes in one step\n                self.showAlert(message: "try again", title: "Error")\n                return\n            }\n\n            self.classesList = classes                                 // otherwise, proceed as normal\n            self.classesCollectionView.reloadData()\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

是的,这重新引入了self我们上面删除的引用,但总的来说,这对于异步请求来说是一个很好的模式:我们永远不会让某些旨在更新视图控制器的网络请求阻止该视图控制器在用户关闭时被释放它。

\n

上述模式的进一步细化是设计fetchAllClasses为可取消的,然后在视图控制器\xe2\x80\x99s 中deinit取消任何挂起的网络请求(如果有)。这超出了这个问题的范围,但我们的想法是,我们不仅不应保留视图控制器超过必要的时间,而且还应该取消挂起的请求。不过,这种deinit取消模式仅在您weak在闭包中使用引用时才有效。

\n