Почему, по вашему мнению, существует цикл удержания? Fetcher
не хранит принятое в completion
ссылку. Если вы запустите следующую команду в детскую площадку:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
class Fetcher {
func fetch(completion: [String] ->()) {
dispatch_async(dispatch_get_main_queue()) {
print("fetching")
completion([])
}
}
deinit { print("deinit Fetcher") }
}
class ViewController: UIViewController {
let fetcher = Fetcher()
func fetch() {
fetcher.fetch(didFetch)
}
func didFetch(result: [String]) {
print("did fetch")
}
deinit { print("deinit ViewController") }
}
var vc: ViewController? = ViewController()
vc?.fetch()
vc = nil
... вы будете видеть все отпечатки, как должно:
fetching
did fetch
deinit ViewController
deinit Fetcher
Однако, вы были хранить ссылки на какой-либо причине:
class Fetcher {
var completion: ([String] ->())?
func fetch(completion: [String] ->()) {
self.completion = completion // causing retain cycle
completion([])
}
}
Тогда вы действительно есть сохранить цикл и два объекта были бы не deinit
...
Редактировать
Этот вопрос растет на меня. В частности, поскольку @noescape
в настоящее время невозможно обеспечить соблюдение при переходе в асинхронное выполнение, насколько мы можем приблизиться к такому идеалу? Есть два подхода, которые приходят на ум, ни один из которых, к сожалению, не дает компилятору гарантий, гарантированных абоненту, хотя они все еще находятся в духе первоначального вопроса в том смысле, что они не включают прохождение в закрытии:
protocol CompletionHandler : AnyObject { // only needed for `fetch2`
associatedtype Result
func onCompletion(_: Result)
}
extension Fetcher {
func fetch2 <H: CompletionHandler where H.Result == [String]> (handler: H) {
dispatch_async(dispatch_get_main_queue()) { [weak handler] in
print("fetching2")
handler?.onCompletion([])
}
}
func fetch3 <T: AnyObject> (handler: T, curry: T -> [String] ->()) {
dispatch_async(dispatch_get_main_queue()) { [weak handler] in
print("fetching3")
if let handler = handler {
curry(handler)([])
}
}
}
}
... который может быть использован как так (где fetch2
предполагает соответствие с CompletionHandler
):
fetcher.fetch2(self)
fetcher.fetch3(self, curry: ViewController.onCompletion)
... с эффектом:
var vc: ViewController? = ViewController()
vc?.fetch()
vc = nil
... печать:
deinit ViewController
deinit Fetcher
fetching2
fetching3
(сравните с выходом консоли первого решения выше и увидеть обсуждение с @Sulthan в комментариях ниже)
Возможно, вы могли бы удалить ссылку на «кэрри функции» ы в вас названии, поскольку нет никакого упоминания о них в вопрос? – milos