【问题标题】:Understanding Swift Closure Capturing了解 Swift 闭包捕获
【发布时间】:2017-06-13 08:35:11
【问题描述】:

我经历了Swift ClosuresARC in Swift,我有点困惑。

我有调用 Web 服务和使用响应数据的简单场景。
这是我的基本实现:

class WebServices: NSObject {
  func requestDataFromServer(completion: @escaping (_ data: Data?) -> Void) {
    //web service call here
    completion(Data())
  }
  deinit {
    print("WebServices deinitializer...")
  }
}

class Controller: NSObject {
  private let webService = WebServices()
  private func useResponseData(_ data: Data) {
    print("Response Data: \(data)")
  }
  func fetchData() {
    webService.requestDataFromServer { (data) in
      if let responseData = data {
        self.useResponseData(responseData)//direct use of self
      }
    }
  }
  deinit {
    print("Controller deinitializer...")
  }
}

var controller: Controller? = Controller()
controller!.fetchData()
controller = nil

控制台输出是:

响应数据:0 字节
控制器去初始化器...
WebServices deinitializer...

我的问题是即使我在闭包内直接使用self,为什么这个实现不会导致参考保留周期
如果我使用unownedweak,那么也是同样的行为。


在上述情况下,什么会导致引用保留循环?(我不想引起,而是想知道错误)

【问题讨论】:

    标签: swift closures self-reference


    【解决方案1】:

    您的代码中没有问题,因为requestDataFromServer直接调用完成处理程序(无异步)。所以来电者不能在你通话期间被释放。

    但是当您实现 web 服务的真实调用时,它将是异步的。因此用户可以在您的网络服务回答之前切换页面。在这种情况下,您将保留对控制器的强引用,并且它永远不会被释放。你应该在你的闭包中使用[weak self](所以调用self?.useResponseData(responseData),self 是可选的)。

    unowned 用于您确定您的引用不会为零(它不是可选的)

    【讨论】:

      【解决方案2】:

      好吧,我想问题是你在这里做的实际上不是异步的,所以它一个接一个地执行,这段代码导致内存泄漏:

      import Foundation
      
      class WebServices {
          func requestDataFromServer(completion: @escaping (_ data: Data?) -> Void) {
              //web service call here
              print("called")
              DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
                  print("called async")
                  completion(Data())
              })
          }
          deinit {
              print("WebServices deinitializer...")
          }
      }
      
      class Controller: UIViewController {
          private let webService = WebServices()
          private func useResponseData(_ data: Data) {
              print("Response Data: \(data)")
          }
          func fetchData() {
              webService.requestDataFromServer { (data) in
                  print("called")
                  if let responseData = data {
                      self.useResponseData(responseData)//direct use of self
                  }
              }
              self.dismiss(animated: true, completion: nil)
          }
          deinit {
              print("Controller deinitializer...")
          }
      }
      
      var controller: Controller? = Controller()
      controller!.fetchData()
      

      【讨论】:

      • 即使在这种异步情况下,两个实例也在反初始化!
      • @DashAndRest 已编辑,我的错误,现在是 UIViewContoller 类似行为的示例,dismiss 而不是 nil
      • 但这怎么还在反初始化呢?以及它是如何泄漏内存的?
      • @DashAndRest 我的猜测是这与UIViewController生命周期有关,它不直接分配给nil,当viewDidDisappear,所以当它后面还有引用时,它永远不会deinit,当你这样做时,weak self 没有。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      • 2015-10-06
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      相关资源
      最近更新 更多