Structs that refer to each other in Swift 3

As mentionned here: indirect enums and structs, an elegant solution could be to wrap your reference inside an enum with an indirect case.

In you example, it would give this:

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
}

Beyond of my understanding, indirect tells the compiler there is a recursion in your enum, and it should insert the necessary layer of indirection.


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

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

You can circumvent 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 relationship covered above is not directly related to ARC (automatic reference counting) or strong reference cycles, but the fact that the value of a value type instance is the instance itself and the value of all value type (sub-)properties it contains (as well as the reference to all reference type (sub-)properties it contains).

Just take care if you choose to let both your one to one entities be reference types: in this case you must make sure that one of the references between the two types are weak. E.g.:

class DetailedPin {
    weak var pin: Pin?
}

class Pin {
    var detailedPin: DetailedPin?
}

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

class Pin {
    weak var detailedPin: DetailedPin?
}

In case you leave out weak above (i.e., let both refer to each other by the default strong references), you will have a strong reference cycle between the two instances referring 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

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")

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

Usage:

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

pin.detailedPin = detailedPin

Tags:

Struct

Swift