Last active
August 27, 2019 15:14
-
-
Save valeriyvan/5268d656ececbb666f15c4fc4a60adf1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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