Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retain cycle between class and struct

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?

like image 648
user3581248 Avatar asked Mar 06 '17 17:03

user3581248


People also ask

Can struct Cause retain cycle?

Sometimes structs and enums can be treated as reference types, and this means that retain cycles can occur in structs and enums too.

What is a retain cycle?

A retain cycle (also known as a reference cycle) is when two objects hold a strong reference to one another. An example of this is shown below: Object A is holding a strong reference to Object B , and Object B is also holding a strong reference to Object B .

Which of the following can cause a retain cycle?

Structs and Classes A choice between a struct and a class can cause a retain cycle. Both structs and classes can have constants, variables, functions and protocols.

What's retain cycle how you can avoid?

In order to prevent this retain cycle, we need to declare at least one of the variable as weak or unowned. We can break the retain cycle with adding a weak keyword before either the driver property of the Car class or the car property of the Person class.


3 Answers

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

like image 70
Martin R Avatar answered Dec 08 '22 17:12

Martin R


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.

like image 26
Edgar Avatar answered Dec 08 '22 17:12

Edgar


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
}
like image 43
Wasim Avatar answered Dec 08 '22 19:12

Wasim