我有两个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
.和Pin
struct 一样的错误.我该如何处理这个问题?谢谢.
Ham*_*ish 29
问题是,Optional
它的Wrapped
内联值存储(有关此内容的更多信息,请参阅Mike Ash的精彩博文) - 意味着Optional
实例(无论是否nil
存在)将占用至少与您希望的类型相同的内存量存储在它的.some
情况下(Wrapped
类型).
因此,由于您的Pin
struct具有type属性DetailedPin?
,并且DetailedPin
具有type属性,Pin?
因此需要无限存储才能将这些值内联存储.
因此,解决方案只是添加一个间接层.这样做的一种方法是制作Pin
和/或DetailedPin
引用类型(即a class
),如@dfri所建议的那样.
但是,如果你想保留的值语义Pin
和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)
}
}
}
}
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)
仅当两个实体之间的连接中的至少一个是引用类型时,两个实体之间的一对一关系才有效。对于两种纯价值类型,它们的一对一关系将成为递归关系。
假设您创建了一个值类型的对象DetailedPin
。它包含一个实例属性(pin
值类型的)Pin
,这意味着该实例属性是值的一部分也就是实例DetailedPin
。现在,和的实例属性pin
具有DetailedPin
值类型Pin
,其本身包含值类型的实例属性(detailedPin
)DetailedPin
。该实例成员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)
归档时间: |
|
查看次数: |
5259 次 |
最近记录: |