我们都知道,在Swift的传统并发中,如果你在一个类中执行(例如)一个网络请求,在完成该请求时,你引用了一个属于该类的函数,你必须传入[weak self],如下所示:

func performRequest() {
   apiClient.performRequest { [weak self] result in
      self?.handleResult(result)
   }
}

这是为了阻止我们在闭包时强烈捕获self个,并导致不必要的保留/无意中引用已经从内存中丢失的其他实体.

使用async/await怎么样?我在网上看到了相互矛盾的事情,所以我将向社区发布两个例子,看看你对这两个方面的看法:

class AsyncClass {
   func function1() async {
      let result = await performNetworkRequestAsync()
      self.printSomething()
   }

   func function2() {
      Task { [weak self] in
         let result = await performNetworkRequestAsync()
         self?.printSomething()         
      }
   }

   func function3() {
      apiClient.performRequest { [weak self] result in
         self?.printSomething()
      }
   }

   func printSomething() {
      print("Something")
   }
}

function3很简单——老式的并发意味着使用[weak self].

推荐答案

如果你在一个类中执行(例如)一个网络请求,并且在完成该请求时引用了一个属于该类的函数,那么你必须像这样传入[weak self]

这不完全是真的.在Swift中创建闭包时,默认情况下会保留闭包引用的变量或"closes over",以确保调用闭包时这些对象有效.当self在闭包内部被引用时,这包括self.

想要避免的典型保留周期需要两件事:

  1. 关闭保留self个,以及
  2. 100 retains the closure back

如果self强保持闭包,并且闭包强保持self,则会发生保留循环——默认情况下,在没有进一步干预的情况下,两个对象都不能被释放(因为有东西保留了它),因此内存将永远不会被释放.

打破这种循环有两种方法:

  1. 在调用完闭包后,显式断开闭包和self之间的链接,例如,如果self.action是引用self的闭包,则在调用后将nil分配给self.action,例如.

    self.action = { /* Strongly retaining `self`! */
        self.doSomething()
    
        // Explicitly break up the cycle.
        self.action = nil
    }
    

    这通常不适用,因为它一次可以生成self.action个,而且在调用self.action()之前,您还有一个保留周期.或者,

  2. 让其中一个对象保留另一个对象.通常,这是通过确定哪个对象是父子关系中另一个对象的所有者来实现的,通常,self最终强烈地保留闭包,而闭包通过weak self弱地引用self,以避免保留它

这些规则适用于self,以及闭包的作用:网络调用、动画回调等.

对于原始代码,如果apiClientself的成员,则实际上只有一个保留周期,并在网络请求期间保持关闭:

func performRequest() {
   apiClient.performRequest { [weak self] result in
      self?.handleResult(result)
   }
}

如果闭包实际上是在其他地方调度的(例如,apiClient不直接保留闭包),那么实际上不需要[weak self],因为从来没有一个循环开始!

规则exactly与Swift并发和Task相同:

  1. 传递到Task以初始化它的闭包将保留默认情况下它引用的对象(除非使用[weak ...])
  2. Task在任务期间(即,在任务执行时)保持关闭状态
  3. 如果self在执行期间保持Task,则将有一个保留周期

function2()的情况下,Task被旋转起来并异步调度,但self does not hold on被调度到生成的Task对象,这意味着不需要[weak self].如果function2()存储了创建的Taskthen,则可能会有一个需要打破的保留周期:

class AsyncClass {
    var runningTask: Task?

    func function4() {
        // We retain `runningTask` by default.
        runningTask = Task {
            // Oops, the closure retains `self`!
            self.printSomething()
        }
    }
}

如果你需要保留任务(例如,这样你就可以保留cancel个),你会希望避免让任务保留self(Task { [weak self] ... }).

Ios相关问答推荐

如何在react 本机模块中使用Bundle.main.Path()在SWIFT中导入assets资源

Swift在SceneKit中为scnnode添加自定义几何体(视图)

如何使用参与者允许并行读取,但阻止对SWIFT中资源的并发读取和写入?

Xcode 15阻止我的应用程序运行:由于实时模式录制的高比率,丢失了1条日志(log)/路标消息?

安装新版本 XCode 15.0 后无法运行应用程序 XCode

flutter中实现自定义底部导航栏

不使用故事板创建 iMessage 应用程序

错误地提取问题的答案选项

SwiftUI 每秒从 Timer 更新单个 @State 属性,每秒也重绘整个视图

我需要二进制文件来进行 App Store 应用程序传输吗?

UIViewRepresentable - MKMapView 没有得到更新

SwiftUI - 使用切换switch 切换键盘类型(默认/数字)?

在 iPad 上删除列表的顶部和底部行

iOS 中的 [Class new] 和 [[Class alloc] init] 有什么区别?

快速禁用 UITextfield 的用户输入

如何设置imageView的圆角半径?

在 Xcode 5 中为超级视图添加间距约束

使用 AVAudioPlayer 播放声音

在滚动视图中使用动态大小的控制器调整容器视图的大小

使用 Xcode 的 All Exceptions 断点时忽略某些异常