繁体   English   中英

Swift 3 中相互引用的结构

[英]Structs that refer to each other in Swift 3

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

struct DetailedPin {
     var pin: Pin?
}

struct Pin {
    var detailedPin: DetailedPin?  
}

但是我得到了一个错误: Value type 'DetailedPin' cannot have a stored property that references itself Pin结构也有同样的错误。 我该如何处理这个问题? 谢谢。

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

因此,由于您的Pin结构具有一个类型为 DetailsPin 的属性DetailedPin? ,而DetailedPin有一个Pin?类型的属性Pin? ,需要无限存储才能内联存储这些值。

因此,解决方案是简单地添加一个间接层。 这样做的一种方法是使Pin和/或DetailedPin成为@dfri 建议的引用类型(即class )。

但是,如果您希望保留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)
      }
    }
  }
}

您现在可以只对一个(或两个)结构属性使用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")

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

假设您创建了一个值类型为DetailedPin的对象。 它包含一个实例属性( pin值类型的) Pin ,这意味着该实例属性是是实例的值的一部分DetailedPin 现在, DetailedPin和 instance 的实例属性pin是值类型Pin ,它本身包含值类型DetailedPin的实例属性( detailedPin )。 这个实例成员detailedPin是,再有,就是实例的值_a一部分pin ),但同样, detailedPin本身拥有类型的值pin ,所以递归舞蹈继续...

您可以通过将您的结构之一转换为引用类型( class )来规避此问题:

struct DetailedPin {
   var pin: Pin?
}

class Pin {
    var detailedPin: DetailedPin?
}

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

struct Pin {
    var detailedPin: DetailedPin?
}

注意,以上所覆盖的递归关系不直接相关的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?
}

如果您省略了上面的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

正如这里提到的: indirect enums and structs ,一个优雅的解决方案可能是将您的引用包装在一个带有indirect案例的枚举中。

在你的例子中,它会给出:

enum ReferencePin {
  case none
  indirect case pin(Pin)
}

struct DetailedPin {
     var pin: ReferencePin
}

struct Pin {
    var detailedPin: DetailedPin?
}

或直接,取决于您的需要:

enum ReferencePin {
  case none
  indirect case pin(Pin)
}

struct Pin {
    var otherPin: ReferencePin
}

超出我的理解, indirect告诉编译器在您的枚举中存在递归,它应该插入必要的间接层

这应该完成你需要的:

struct DetailedPin {
    private var _pin: Any?

    var pin: Pin? {
        get {
            return _pin as? Pin
        }
        set {
            _pin = newValue
        }
    }
}

struct Pin {
    private var _detailedPin: Any?

    var detailedPin: DetailedPin? {
        get {
            return _detailedPin as? DetailedPin
        }
        set {
            _detailedPin = newValue
        }
    }
}

用法:

var pin = Pin()
var detailedPin = DetailedPin()

pin.detailedPin = detailedPin

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM