在Swift 3中相互引用的结构

Rom*_*use 12 struct swift

我有两个CoreData实体,它们具有一对一的关系.我想基于这个实体创建结构. 我的代码:

struct DetailedPin {
     var pin: Pin?
}

struct Pin {
    var detailedPin: DetailedPin?  
}
Run Code Online (Sandbox Code Playgroud)

但是我收到了一个错误:Value type 'DetailedPin' cannot have a stored property that references itself.和Pinstruct 一样的错误.我该如何处理这个问题?谢谢.

Ham*_*ish 29

问题是,Optional它的Wrapped内联值存储(有关此内容的更多信息,请参阅Mike Ash的精彩博文) - 意味着Optional实例(无论是否nil存在)将占用至少与您希望的类型相同的内存量存储在它的.some情况下(Wrapped类型).

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

因此,解决方案只是添加一个间接层.这样做的一种方法是制作Pin和/或DetailedPin引用类型(即a class),如@dfri所建议的那样.

但是,如果你想保留的值语义PinDetailedPin,其中一个方案是创建一个类实例的支持,以提供必要的间接的包装类型:

/// 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)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以只使用Indirect包装器来构建一个(或两个)结构属性:

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")
Run Code Online (Sandbox Code Playgroud)


dfr*_*fri 5

仅当两个实体之间的连接中的至少一个是引用类型时,两个实体之间的一对一关系才有效。对于两种纯价值类型,它们的一对一关系将成为递归关系。

假设您创建了一个值类型的对象DetailedPin。它包含一个实例属性(pin值类型的)Pin,这意味着该实例属性是值的一部分也就是实例DetailedPin。现在,和的实例属性pin具有DetailedPin值类型Pin,其本身包含值类型的实例属性(detailedPinDetailedPin。该实例成员detailedPin再次是_a的一部分,即instance pin),但是detailedPin它本身又拥有type的值pin,因此递归舞蹈继续进行……

您可以通过将其中一个结构转换为引用类型(class)来避免这种情况:

struct DetailedPin {
   var pin: Pin?
}

class Pin {
    var detailedPin: DetailedPin?
}

// or
class DetailedPin {
   var pin: Pin?
}

struct Pin {
    var detailedPin: DetailedPin?
}
Run Code Online (Sandbox Code Playgroud)

注意,以上所覆盖的递归关系不直接相关的ARC(自动引用计数)或强参考周期,但事实证明,一个值类型实例的值是实例本身和的所有值类型(子)属性它包含(以及作为参考的所有参考类型(子)属性它包含)。

请注意,如果您选择将一对一实体都设为引用类型:在这种情况下,必须确保两种类型之间的引用之一为weak。例如:

class DetailedPin {
    weak var pin: Pin?
}

class Pin {
    var detailedPin: DetailedPin?
}

// or
class DetailedPin {
    var pin: Pin?
}

class Pin {
    weak var detailedPin: DetailedPin?
}
Run Code Online (Sandbox Code Playgroud)

如果您忽略weak上面的内容(即,通过默认的强引用互相引用),则在两个彼此引用的实例之间将有一个强引用循环

class DetailedPin {
    var pin: Pin?
    deinit { print("DetailedPin instance deinitialized") }
}

class Pin {
    var detailedPin: DetailedPin?
    deinit { print("Pin instance deinitialized") }
}

func foo() {
    let pin = Pin()
    let detailedPin = DetailedPin()
    pin.detailedPin = detailedPin
    detailedPin.pin = pin
}

foo() // no deinit called
Run Code Online (Sandbox Code Playgroud)