I have two CoreData entities which have one to one relationship. I want to create structs based on this entities. My code:
struct DetailedPin {
var pin: Pin?
}
struct Pin {
var detailedPin: DetailedPin?
}
But I got an error: Value type 'DetailedPin' cannot have a stored property that references itself
. And the same error for Pin
struct. How can I handle this issue? Thanks.
Swift allows us to create property and methods that belong to a struct, rather than an instance of the struct. This means you can access the properties or methods even if the instance of struct is not created. These shared properties in swift are called static properties.
A struct cannot inherit from another kind of struct, whereas classes can build on other classes. You can change the type of an object at runtime using typecasting. Structs cannot have inheritance, so have only one type. If you point two variables at the same struct, they have their own independent copy of the data.
In Swift, instances of classes are passed by reference. This is similar to how classes are implemented in Ruby and Objective-C. It implies that an instance of a class can have several owners that share a copy. Instances of structures and enumerations are passed by value.
Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment.
The problem is that an Optional
stores its Wrapped
value inline (see Mike Ash's fantastic blog post for more info about this) – meaning that an Optional
instance (regardless of whether it is nil
or not) will occupy at least the same amount of memory as the type you wish to store in its .some
case (the Wrapped
type).
Thus, as your Pin
struct has a property of type DetailedPin?
, and DetailedPin
has a property of type Pin?
, infinite storage would be required in order to store these values inline.
The solution therefore is simply to add a layer of indirection. One way of doing this would be to make Pin
and/or DetailedPin
a reference type (i.e a class
) as @dfri has suggested.
However, if you wish to keep the value semantics of Pin
and DetailedPin
, one option would be to create a wrapper type backed by a class instance to provide the necessary indirection:
/// 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)
}
}
}
}
You can now just use the Indirect
wrapper for one (or both) of your structs properties:
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")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With