I am trying to better understand ARC and am using Apples Documentation
Going through the first example I do not get the expected result that Apple states; "Because a weak reference does not keep a strong hold on the instance it refers to, it’s possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated."
Im using a playground in XCode 8.3.2
import UIKit
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil //This prints "John Appleseed is being deinitialized" (as expected)
unit4a?.tenant?.name //This shows "John Appleseed" (expected nil)
unit4a = nil //Prints "Unit4a is being deinitialized" (as expected)
I understand that this prevents the strong reference cycle so that both can be deinitialized but I'm not understanding why unit4a keeps a reference to the tenant?
You're getting deceived by the playgrounds' output on the right which is not in the same time order as your prints (as in the deinits). If you replace your standalone "echoes" with actual print calls, you'll see that in the console, the tenant gets released after you print unit4a?.tenant?.name and not before. This is because the release of weak objects doesn't happen instantly, but on the next run loop. import UIKit import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4a: Apartment?
john = Person(name: "John Appleseed")
unit4a = Apartment(unit: "4A")
john!.apartment = unit4a
unit4a!.tenant = john
john = nil
print(unit4a?.tenant?.name)
unit4a = nil
Output:
Optional("John Appleseed")
John Appleseed is being deinitialized
Apartment 4A is being deinitialized
If however you change the last bits to:
john = nil
DispatchQueue.main.asyncAfter(deadline: .now()) {
print(unit4a?.tenant?.name) //This shows "John Appleseed" (expected nil)
unit4a = nil //Prints "Unit4a is being deinitialized" (as expected)
}
The output is what you would expect:
John Appleseed is being deinitialized
nil
Apartment 4A is being deinitialized
In most cases you wouldn't care that the object is released right away, but if you do, check out autorelease pools