具有值类型的参考周期?

时间:2016-07-04 19:46:49

标签: swift automatic-ref-counting value-type reference-counting reference-type

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

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

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

struct A {
  var otherA: A? = nil
  init() {
    otherA = A()
  }
}

6 个答案:

答案 0 :(得分:10)

引用周期(或保留周期)之所以如此命名,是因为它表示cycle中的object graph

retain cycle

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

在捕获和存储值类型(结构和枚举)时,不存在引用。值是复制,而不是引用,尽管值可以保存对象的引用。

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

答案 1 :(得分:5)

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

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

enum Node {
    case None(Int)
    indirect case left(Int, Node)
    indirect case right(Int, Node)
    indirect case both(Int, Node, Node)
}

答案 2 :(得分:4)

免责声明:我在这里做了一个(希望受过教育的)Swift编译器内部工作的猜测,所以应用盐粒。

除了价值语义之外,问问自己:为什么我们有结构?有什么好处?

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

为了使编译器能够解决这个问题,在确定堆栈或对象框架的结构时,它需要知道给定结构值保留多少空间。只要struct值是固定大小的树(忽略对象的传出引用;它们指向堆对我们不感兴趣),这很好:编译器可以只添加它找到的所有大小。

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

Nota bene:同样的推理解释了为什么结构是按值传递的:我们需要 它们的新环境中

答案 3 :(得分:2)

您通常不能使用值类型的引用循环,因为Swift通常不允许引用值类型。一切都被复制了。

但是,如果你很好奇,你实际上可以通过在闭包中捕获self来诱导一个值类型的引用循环。

以下是一个例子。请注意,MyObject类仅用于说明泄漏。

class MyObject {
    static var objCount = 0
    init() {
        MyObject.objCount += 1
        print("Alloc \(MyObject.objCount)")
    }

    deinit {
        print("Dealloc \(MyObject.objCount)")
        MyObject.objCount -= 1
    }
}

struct MyValueType {
    var closure: (() -> ())?
    var obj = MyObject()

    init(leakMe: Bool) {
        if leakMe {
            closure = { print("\(self)") }
        }
    }
}

func test(leakMe leakMe: Bool) {
    print("Creating value type.  Leak:\(leakMe)")
    let _ = MyValueType(leakMe: leakMe)
}

test(leakMe: true)
test(leakMe: false)

输出:

Creating value type.  Leak:true
Alloc 1
Creating value type.  Leak:false
Alloc 2
Dealloc 2

答案 4 :(得分:1)

快速简便的黑客解决方法:只需将其嵌入阵列即可。

struct A {
  var otherA: [A]? = nil
  init() {
    otherA = [A()]
  }
}

答案 5 :(得分:0)

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

取决于您的意思"仅限值类型"。 如果你的意思是完全没有包含隐藏内容的引用,那么答案是否定的。 要制作参考周期,您至少需要一个参考。

但是在Swift中,Array,String或其他类型是值类型,它们可能在其实例中包含引用。如果您的"值类型"包括这些类型,答案是肯定的。