【问题标题】:Lifetime of retained memory in Swift closuresSwift 闭包中保留内存的生命周期
【发布时间】:2014-08-11 09:19:09
【问题描述】:

在 WWDC 2014 的高级 Swift 演讲中,演讲者给出了这个使用泛型的函数记忆器的例子:

func memoize<T: Hashable, U>( body: (T)->U ) -> (T)->U {
    var memo = Dictionary<T, U>()
    return { x in
        if let q = memo[x] { return q }
        let r = body(x)
        memo[x] = r
        return r
    }
}

我无法理解 memo var 的生命周期。记忆化的fibonacci 函数的每次调用是否都持有对它的强引用?如果是这样,当你完成它时,你会如何释放它?

【问题讨论】:

    标签: generics swift memoization


    【解决方案1】:

    在 C/Objective-C 块术语中,memo 是一个 __block 变量(在 Swift 中,您不需要显式编写 __block 来通过引用捕获变量)。该变量可以在一个块(闭包)中分配,并且所有看到该变量的范围都将看到来自任何其他变量的更改(它们共享对该变量的引用)。只要使用它的某个块(闭包)(在这种情况下只有一个块使用它)仍然存在,该变量将是有效的。在使用它的最后一个块被释放后,变量超出范围。它的工作原理是一个实现细节。

    如果此变量具有对象指针类型,则指向的对象将被捕获它的任何块保留。但是,在这种情况下,变量是Dictionary,一个结构类型,它是一个值类型。因此,无需担心内存管理。变量就是结构体,结构体的寿命与变量一样长。 (结构体本身可能会在其他地方分配内存,并在其析构函数中释放它,但这完全由结构体内部处理,外部不应该知道或关心它。)

    通常不需要担心__block 变量在内部是如何工作的。但基本上,变量被包装在一种简化的“对象”中,实际的“变量”是这个“对象”的一个字段,它是通过引用计数进行内存管理的。捕获它们的块持有对这个伪对象事物的“强引用”——当在堆上创建一个块时(从技术上讲,当它们从堆栈块复制到堆时)使用这个 __block 变量,它增加了参考计数;当使用它的块被释放时,它会减少引用计数。当引用计数变为 0 时,这个伪“对象”被释放,首先为其变量类型调用适当的析构函数。

    为了回答您的问题,“记忆斐波那契函数”是一个块(闭包)。正是它对拥有memo 变量的任何东西都有很强的引用。 “调用”没有强引用或弱引用;调用函数时,它使用函数本身具有的引用。在这种情况下,memo 变量的生命周期就是“记忆斐波那契函数”的生命周期,因为它是唯一捕获此变量的闭包。

    【讨论】:

      【解决方案2】:

      memoize() 的每次调用都将创建自己的 memo 变量,独立于其他调用。只要引用它的闭包存在,它就会存在;当闭包被释放时,它捕获的变量(在本例中为memo)也被释放。

      您可以将其视为返回如下伪代码中的结构:

      struct Closure<T,U>
      {
          var memo: Dictionary<T, U>
          func call(t: T): U
          {
              ....
          }
      }
      
      func memoize<T: Hashable, U>( body: (T)->U ) -> Closure<T,U>
      {
          return Closure(memo: Dictionary<T, U>())
      }
      

      【讨论】:

      • 和那个代码不一样,因为多个闭包可以捕获这个变量,并且这个变量将在所有闭包之间共享状态。另外,闭包和闭包外的代码也共享变量状态(即memoize函数中的代码也可以直接读写memo)。
      • @newacct 代码是为了说明这种特殊情况,而不是一般的闭包。
      【解决方案3】:

      内部块(返回值)将保留备忘录,这意味着只要保留返回的块,备忘录就会持续存在。

      每次调用 memoize 函数时都会创建一个新的 memo 实例。调用返回的块不会创建新的备忘录实例。

      当返回的块超出范围时,内存将全部释放。

      【讨论】:

        猜你喜欢
        • 2018-05-13
        • 2011-03-14
        • 2018-11-15
        • 1970-01-01
        • 1970-01-01
        • 2012-08-27
        • 1970-01-01
        • 2020-03-22
        • 1970-01-01
        相关资源
        最近更新 更多