具有值类型的参考周期?

Chr*_*res 11 reference-counting value-type reference-type automatic-ref-counting swift

当引用类型的属性具有相互较强的所有权(或具有闭包)时,会发生Swift中的引用循环.

但是,是否有可能使用值类型的参考周期?


我在游乐场尝试了这个没有成功(错误:不允许递归值类型'A').

struct A {
  var otherA: A? = nil
  init() {
    otherA = A()
  }
}
Run Code Online (Sandbox Code Playgroud)

jtb*_*des 11

参考周期(或保留周期)被如此命名是因为它表明一个周期中的对象图:

保留周期

每个箭头表示一个对象保留另一个(强引用).除非循环被破坏,否则永远不会释放这些对象的内存.

捕获和存储值类型(结构和枚举)时,没有引用这样的东西.尽管值可以保存对象的引用,但是值被复制而不是引用.

换句话说,值可以在对象图中具有外向箭头,但没有传入箭头.这意味着他们无法参与一个周期.


mat*_*att 5

正如编译器告诉你的那样,你要做的是非法的.正是因为这一种价值类型,没有连贯,有效的方式来实现你所描述的内容.如果类型需要引用自身(例如,它具有与自身类型相同的属性),则使用类,而不是结构.

或者,您可以使用枚举,但只能以特殊的,有限的方式使用:枚举大小写的关联值可以是该枚举的实例,前提是大小写(或整个枚举)indirect:

enum Node {
    case None(Int)
    indirect case left(Int, Node)
    indirect case right(Int, Node)
    indirect case both(Int, Node, Node)
}
Run Code Online (Sandbox Code Playgroud)

  • @AlfieHanssen这是考虑它的一种方式.请记住这些是值类型:赋值复制.现在想象一下结构`struct Dog {var puppy:Dog?}`.现在考虑这段代码:`let d = Dog(); d.puppy = d`.显然这是不连贯的:这是理发师的悖论.因此,斯威夫特只是放下脚步,禁止整个局势. - 现在,实际上,还有更多的东西; 它与值类型实际上如何保存在内存中有关.但至少这会给你一个动力. (4认同)

Rap*_*ael 5

免责声明:我在这里对 Swift 编译器的内部工作进行了(希望受过教育的)猜测,因此请谨慎使用。

除了值语义,问问自己:为什么我们有结构?优势是什么?

一个优点是我们可以(阅读:想要)将它们存储在堆栈上(分别在对象框架中),即就像其他语言中的原始值一样。特别是,我们不想在堆上分配专用空间来指向。这使得访问结构值更有效:我们(阅读:编译器)总是知道它在内存中找到值的确切位置,相对于当前帧或对象指针。

为了让编译器解决这个问题,在确定堆栈或对象帧的结构时,它需要知道为给定的结构值保留多少空间。只要结构值是固定大小的树(不考虑对对象的传出引用;它们指向堆对我们来说不感兴趣),那很好:编译器可以将它找到的所有大小相加。

如果您有递归结构,这将失败:您可以通过这种方式实现列表或二叉树。编译器无法静态地弄清楚如何将这些值存储在内存中,因此我们必须禁止它们。

注意:同样的推理解释了为什么结构是按值传递的:我们需要它们在物理上处于新的上下文中。