【问题标题】:convert the reference back to a strong one inside the closure, memory management, swift将引用转换回闭包内的强引用,内存管理,swift
【发布时间】:2016-08-05 01:37:19
【问题描述】:

我正在像下面这样在关闭中试验保留周期

 class Sample {
        deinit {
            print("Destroying Sample")
        }

        func additionOf(a: Int, b:Int) {
            print("Addition is \(a + b)")
        }

        func closure() {                
          dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
            self?.additionOf(3, b: 3)   
            usleep(500)                 
            self?.additionOf(3, b: 5)  
          }
        }
    }

稍后,我正在做

var sample : Sample? = Sample()
sample?.closure()

dispatch_async(dispatch_get_global_queue(0, 0)) {
  usleep(100)
  sample = nil
}

输出将是

Addition is 6
Destroying Sample

这是可以理解的,因为self 在执行self?.additionOf(3, b:5) 之前为零

如果我通过创建另一个引用 [weak self] 的变量来在闭包内进行更改,如下所示

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
   guard let strongSelf = self else { return }
   strongSelf.additionOf(3, b: 3)
   usleep(500)
   strongSelf.additionOf(3, b: 5)
}

这次的输出是

Addition is 6
Addition is 8
Destroying C

我的问题是为什么strongSelfsample = nil 之后不为零。是不是因为在sample = nil之前的闭包里面被捕获了

【问题讨论】:

  • 当您设置sample = nil 时,您所做的就是解决对Sample 的一个特别强的引用。但它对可能存在的任何其他强引用没有影响(例如strongSelf,它是在您执行sample = nil 之前分配的)。在[weak self] 闭包中执行strongSelf 模式(有时被戏称为“strongSelf/weakSelf dance”)的全部目的是确保如果Sample 在闭包开始时没有被释放,它将保持直到关闭完成。
  • @Rob:我想我理解但不是 100%。从 OP 开始,当关闭开始时,直到执行第一个 additionOf(3, b: 3) 之前,Sample 还没有被释放。我仍然不明白为什么 strongSelf 没有改变,因为 self 为零。除非它被捕获在闭包内??
  • 并不是被闭包“捕获”了。简单地说,闭包已经建立了对Sample 对象的新的第二个强引用。因此,当您致电var sample : Sample? = Sample() 时,有一个强有力的参考。当代码遇到guard let strongSelf = self else ...时,即建立了第二个强引用,一共是两个强引用。当您在主线程上执行sample = nil 时,还剩下一个强引用。只有当strongSelf 超出范围时,才会解析这个最终的强引用并释放对象。

标签: memory-management swift2 closures weak-references strong-references


【解决方案1】:

让我们考虑你的第二个例子:

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
    guard let strongSelf = self else { return }
    strongSelf.additionOf(3, b: 3)
    usleep(500)
    strongSelf.additionOf(3, b: 5)
}

这是一种常见的模式,它有效地表示“如果self 已被释放,则立即return,否则建立强引用strongSelf,并保持此强引用直到关闭完成。”

所以,在你的例子中,当这个分派的块开始时,self 不是nil,所以一旦我们分配strongSelf 来引用它,我们现在有两个对这个Sample 对象的强引用,原来的 sample 引用和这个闭包内的这个新的 strongSelf 引用。

因此,当您的其他线程删除它自己的强引用 sample 对此 Sample 对象(通过超出范围或将 sample 显式设置为 nil)时,此 strongSelf 强引用仍然存在,并且会阻止对象被释放(或者至少在这个分派的块完成之前)。

在你上面的评论中,你问:

我仍然不明白为什么 strongSelf 没有改变,因为 self 是 nil out...

强引用永远不会被设置为nil,只是因为其他一些强引用(即使它是最初的强引用)被设置为nil。将引用设置为 nil 的行为仅适用于 weak 引用,不适用于强引用。

当您的代码在第一个线程上执行sample = nil 时,所做的只是删除强引用。它不会删除对象或类似的东西。它只是删除了它的强引用。现在调度块有它自己的强引用,sample = nil 上发生的所有事情就是有两个强引用的对象现在只剩下一个强引用(调度块中的strongSelf 引用)。只有当这个最终的强引用被移除时,对象才会被释放并调用deinit

【讨论】:

    猜你喜欢
    • 2017-08-09
    • 1970-01-01
    • 2021-07-20
    • 2011-06-10
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 2015-11-24
    相关资源
    最近更新 更多