Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active July 3, 2018 20:55
Show Gist options
  • Save rnapier/5b7351988e580e9082a734cd78011061 to your computer and use it in GitHub Desktop.
Save rnapier/5b7351988e580e9082a734cd78011061 to your computer and use it in GitHub Desktop.
Laundering retain loops
// With functions/methods you can "launder" your code so it doesn't require self references anymore,
// but you wind up with retain loops that are very non-obvious. I'm having trouble finding good coding
// styles that reliably avoid this kind of problem.
class B {
// A retain loop waiting to happen
var completionHandler: () -> Void = {}
}
class C {
var value = 0
var b = B()
var otherValue: Int = 0 {
didSet {
// Pretty obviously a retain loop, and the required `self` is a warning to that.
b.completionHandler = { [otherValue] in self.value = otherValue }
}
}
deinit {
print("\(self): DEINIT")
}
}
class C2 {
var value = 0
var b = B()
var otherValue: Int = 0 {
didSet {
// This creates a retain loop, but no "self" is required anywhere
// Removing the reference to `value` removes the retain loop. So the
// caller has to know the internal details the function to know whether
// it captures self.
func setValue(_ newValue: Int) { value = newValue }
func noop(_ newValue: Int) {} // Using this instead does not create a retain loop
b.completionHandler = { [otherValue] in setValue(otherValue) }
}
}
deinit {
print("\(self): DEINIT")
}
}
// No retain loop
var c: C? = C()
c = nil // C: DEINIT
print("If no DEINIT, this is a retain loop\n----")
// Retain loop
c = C()
c?.otherValue = 2
c = nil
print("If no DEINIT, this is a retain loop\n----")
// Retain loop, but no "self" is required
var c2: C2? = C2()
c2?.otherValue = 2
c2 = nil
print("If no DEINIT, this is a retain loop\n----")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment