Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Weakly retaining an object in a closure without having to write [weak]
// I saw this trick in “Do you often forget [weak self], here is a solution”
// https://medium.com/anysuggestion/preventing-memory-leaks-with-swift-compile-time-safety-49b845df4dc6
import UIKit
class ViewController: UIViewController {
// move this variable inside viewDidLoad to see it being released
let downloader = Downloader()
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://baconmockup.com/300/200/")!
// the compiler knows that 'object' will be an instance of 'downloader' without you passing self
downloader.download(url: url, delegate: downloader) { object, data in
object.countBytes(data: data)
}
}
}
class Downloader
{
/**
Download a URL.
- Parameter: delegate Passed to the closure.
- Parameter: Invoked when the object is downloaded. The delegate is already weakly held. You don’t need to use [weak] at the call site.
*/
func download<Delegate: AnyObject>(url: URL, delegate: Delegate, with callback: @escaping (Delegate, Data) -> Void){
let weakCallback: ((Data)->()) = { [weak delegate] data in
guard let delegate = delegate else {
print("the delegate is gone")
return
}
callback(delegate, data)
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
weakCallback(data)
}.resume()
}
func countBytes(data: Data){
print(data)
}
}
@janodev
Copy link
Author

janodev commented Apr 8, 2018

Of course, nothing prevents you from using it incorrectly:

downloader.download(url: url, delegate: downloader) { [weak self] foobar, data in
    self?.downloader.countBytes(data: data)
}

Still, the closure parameter is a reminder not to use [weak self] so I’d say this is a useful idiom.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment