13

Assuming I have following code:

struct X {
    let propertyOfTypeY: Y
}

class Y {
    var propertyOfTypeX: X?
}

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

If these were both classes, then it would mean a retain cycle. However it's not clear to me how the differences between classes and structs apply to the example above. Will it cause a retain cycle, or is it a safe code because of the usage of struct?

user3581248
  • 964
  • 10
  • 22

3 Answers3

15

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

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    Might be useful to note 'struct X { unowned let propertyOfTypeY: Y }' will allow cycle to be broken. – Norman Mar 06 '17 at 17:53
10

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.

Edgar
  • 2,500
  • 19
  • 31
  • 4
    A similar "trick" is to put the code inside a `do { ... }` block and check if deinit is called after executing the block. That works even with non-optionals. – Martin R Mar 06 '17 at 18:00
5

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
}
Wasim
  • 921
  • 12
  • 14