Retain cycle between class and struct

TL;DR There's a retain cycle, but you can see it for yourself!

struct X {
    let propertyOfTypeY: Y
}

class Y {
    var propertyOfTypeX: X?

    deinit {
        print("I was deinit'ed")
    }
}

do {
    let y = Y()
    let x = X(propertyOfTypeY: y)
    y.propertyOfTypeX = x
}
// y and x should be dealloc'ed here, because the "do scope" ends

Comment out y.propertyOfTypeX = x and I was deinit'ed will be printed, But if you do that assignment, deinit is never called.

Same thing can happen if you use a closure.


Memory graph shows reference cycle

enter image description hereThere is definitely a retain cycle.

Solution: It should unowned or weak to break the cycle

struct X {
    unowned let propertyOfTypeY: Y
}

class Y {
    var propertyOfTypeX: X?

    deinit {
        print("Y deallocated")
    }
}

do {
    let y = Y()
    let x = X(propertyOfTypeY: y)
    y.propertyOfTypeX = x
}

Yes, you have a retain cycle.

y.propertyOfTypeX = x

copies the value x to y.propertyOfTypeX, including the property x.propertyOfTypeY which is a reference to y.

Therefore

y.propertyOfTypeX?.propertyOfTypeY === y

holds. What you have is essentially the same as

class Y {
    var propertyOfTypeY: Y?
}

var y = Y()
y.propertyOfTypeY = y

only that propertyOfTypeY is part of a struct X (and that x holds an additional reference to y).