Skip to content

Instantly share code, notes, and snippets.

@valeriyvan
Last active August 27, 2019 15:14
Show Gist options
  • Save valeriyvan/5268d656ececbb666f15c4fc4a60adf1 to your computer and use it in GitHub Desktop.
Save valeriyvan/5268d656ececbb666f15c4fc4a60adf1 to your computer and use it in GitHub Desktop.
import Foundation
// One drawback of using callbacks vs delegation in Swift - if method is used
// instead of closure for callback, retain cycle is created.
// And this retain cycle can't be broken with usual weak/unowned capture.
// In case of delegate everyone knows that delegate property should be weak,
// and linter enforces this.
// Callbacks are nice. But you can't enforce only closures to be used for callbacks.
// And very little people know that method in place of callback creates retain cycle.
// May be linter rule should be created for this.
// In code below there's retain cycle:
// `callback` property in `Worker`, in case it is set with method, implicitly
// strongly catches instance of a class with that method.
// The only way to break this retain cycle - wipe `worker` property in `C` at
// some moment.
// Everything fine if closure is used. Sure if usual in this circumstances
// weak/unowned capture is used as well.
class Worker {
private var callback: (() -> Void)?
init(callback: @escaping () -> Void) {
self.callback = callback
callCallbackInFuture()
}
deinit {
print("\(#function)")
}
private func callCallbackInFuture() {
DispatchQueue.global(qos: .background).async { [weak self] in
self?.callback?()
}
}
}
class C {
private var worker: Worker?
init() {
// If closure us used for callback everything fine.
//self.worker = Worker { [weak self] in
// self?.callback()
//}
// But if method is used for callback we have retain cycle
// which can't be broken with using weak/unowned.
// The only way to break such retain cycle -
// get rid of worker when it's not needed.
self.worker = Worker(callback: callback)
}
deinit {
print("\(#function)")
}
func callback() {
// do something
doSomeWork()
// If worker is not needed after callback is called,
// callback might get rid of worker.
// Otherwise someone from outside should call `removeWorker()`.
// The last isn't very nice.
//removeWorker()
}
private func doSomeWork() {
print("\(#function)")
}
func removeWorker() {
worker = nil
}
}
var c: C? = C()
c = nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment