一尘不染

Swift 3中相互引用的结构

swift

我有两个具有一对一关系的CoreData实体。我想基于此实体创建结构。 我的代码:

struct DetailedPin {
     var pin: Pin?
}

struct Pin {
    var detailedPin: DetailedPin?  
}

但我收到一个错误:Value type 'DetailedPin' cannot have a stored property that references itself。和Pin结构相同的错误。我该如何处理?谢谢。


阅读 236

收藏
2020-07-07

共1个答案

一尘不染

问题在于,一个inline Optional存储其Wrapped值(有关此信息,请参见Mike
Ash的精彩博客文章
)–意味着一个Optional实例(无论是否nil存在)将至少占用与所需类型相同的内存量存储在它的.some大小写(Wrapped类型)中。

因此,由于您的Pin结构具有type属性DetailedPin?,并且type
DetailedPin属性,Pin?因此需要无限存储才能内联存储这些值。

因此,解决方案只是添加一个间接层。一种这样做的方法是按照@dfri的建议进行make
Pin和/或DetailedPin引用类型(即aclass)。

但是,如果您希望保留Pinand 的值语义DetailedPin,则一种选择是创建一个由类实例支持的包装器类型,以提供必要的间接寻址:

/// Provides indirection for a given instance.
/// For value types, value semantics are preserved.
struct Indirect<T> {

  // Class wrapper to provide the actual indirection.
  private final class Wrapper {

    var value: T

    init(_ value: T) {
      self.value = value
    }
  }

  private var wrapper: Wrapper

  init(_ value: T) {
    wrapper = Wrapper(value)
  }

  var value: T {
    get {
      return wrapper.value
    }
    set {
      // Upon mutation of value, if the wrapper class instance is unique,
      // mutate the underlying value directly.
      // Otherwise, create a new instance.
      if isKnownUniquelyReferenced(&wrapper) {
        wrapper.value = newValue
      } else {
        wrapper = Wrapper(newValue)
      }
    }
  }
}

现在,您可以只将Indirect包装器用于您的structs属性中的一个(或两个):

struct DetailedPin {
  private var _pin = Indirect<Pin?>(nil)

  // Convenience computed property to avoid having to say ".value" everywhere.
  var pin: Pin? {
    get { return _pin.value }
    set { _pin.value = newValue }
  }
}

struct Pin {
  var detailedPin: DetailedPin?
  var foo: String
}

var d = DetailedPin()
var p = Pin(detailedPin: d, foo: "foo")
d.pin = p

// testing that value semantics are preserved...
var d1 = d
d1.pin?.foo = "bar"

print(d.pin?.foo as Any) // Optional("foo")
print(d1.pin?.foo as Any) // Optional("bar")
2020-07-07