Structures that reference each other in Swift 3

I have two CoreData objects that are one to one related. I want to create structures based on these objects. My code is:

struct DetailedPin { var pin: Pin? } struct Pin { var detailedPin: DetailedPin? } 

But I got an error message: Value type 'DetailedPin' cannot have a stored property that references itself . And the same error for Pin struct. How can I deal with this problem? Thanks.

+8
struct swift
source share
4 answers

The problem is that Optional stores the value of Wrapped inline (see Mike Ash's blog post for more information on this) - meaning an Optional instance (whether it is nil or not) will occupy at least that same amount of memory as the type that you want to keep in your case .some (type Wrapped ) ,.

So, since your Pin structure has a property of type DetailedPin? , and DetailedPin has a property of type Pin? , storage of these values ​​in a string must require infinite storage.

Thus, the solution simply adds a layer of indirection. One way to do this is to make Pin and / or DetailedPin reference type (i.e. a class ), as @dfri suggested .

However, if you want to preserve the semantics of the Pin and DetailedPin values, one option would be to create a wrapper type supported by the class instance to provide the necessary indirectness:

 // 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, // simply mutate the underlying value directly. // otherwise, create a new instance. if isKnownUniquelyReferenced(&wrapper) { wrapper.value = newValue } else { wrapper = Wrapper(newValue) } } } } 

Now you can simply use the Indirect wrapper for one (or both) of the properties of your structures:

 struct DetailedPin { var pin: Indirect<Pin>? } struct Pin { var foo: String var detailedPin: DetailedPin? } var p = Pin(foo: "foo", detailedPin: nil) var d = DetailedPin(pin: Indirect(p)) p.detailedPin = d // testing that value semantics are preserved... var p1 = d.pin p1?.value.foo = "bar" print(p1?.value.foo as Any) // Optional("bar") print(d.pin?.value.foo as Any) // Optional("foo") print(p.foo) // "foo" 
+17
source share

A one-to-one relationship between two objects is valid only if at least one of the connections between these objects has a reference type; for two pure value types, their one-to-one relationships will become recursive.

Suppose you created an object of type DetailedPin . It contains an instance property ( pin ) of the pin value type, which means that this instance property is part of the value that is the DetailedPin instance. Now the pin instance property and the DetailedPin instance have a pin value type, which itself contains the instance property ( DetailedPin ) of the DetailedPin value type. This member of the DetailedPin instance is again part of the value that is the pin instance), but again, DetailedPin itself owns a pin value, and so the recursive dance continues ...

You can get around this by turning one of your structures into a reference type ( class ):

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

Note that the recursive relation described above is not directly related to ARC (automatic reference counting) or strong reference loops, but the fact that the value of the value type instance is the instance itself and the value of the entire value type (sub -) that it contains (and also a link to all the properties of the reference type (sub) that it contains).

Just take care if you decide that both of your one on one are reference types: in this case, you must make sure that one of the links between the two types is weak . For example:.

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

If you exit weak above (i.e., both link to each other by default strong links), you will have a strong link loop between two instances that link to each other

 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 
+4
source share

This should accomplish what you need:

 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 } } } 

Using:

 var pin = Pin() var detailedPin = DetailedPin() pin.detailedPin = detailedPin 
+2
source share

As mentioned here: indirect enumerations and structures , an elegant solution might be to wrap your link inside an enumeration using the indirect case.

In your example, this will give the following:

 enum ReferencePin { case none indirect case pin(Pin) } struct DetailedPin { var pin: ReferencePin } struct Pin { var detailedPin: DetailedPin? } 

or directly, depending on your needs:

 enum ReferencePin { case none indirect case pin(Pin) } struct Pin { var otherPin: ReferencePin } 

In addition to my understanding, indirect tells the compiler that there is recursion in your enumeration, and it must insert the necessary layer of indirection.

+1
source share

All Articles