GCD сохраняет четкую ссылку на "себя" даже при определении списка захвата
class MyClass {
var someProperty = 0
deinit {
// never gets called
}
func doSomething() {
DispatchQueue.global().async { [weak self] in
Thread.sleep(forTimeInterval: 3600)
self?.someProperty = 123
}
}
}
class MyViewController: UIViewController {
var myClass: MyClass?
deinit {
// MyViewController gets properly deallocated
}
override func viewDidLoad() {
super.viewDidLoad()
myClass = MyClass()
myClass?.doSomething()
}
}
При выполнении вышеуказанного кода MyClass никогда не освобождается, даже когда MyViewController извлекается из стека навигации или удаляется.
Однако, если я перенесу реализацию MyClass непосредственно на MyViewController, все будет работать как положено:
class MyViewController: UIViewController {
var someProperty = 0
deinit {
// MyViewController still gets properly deallocated
}
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async { [weak self] in
Thread.sleep(forTimeInterval: 3600)
self?.someProperty = 123
}
}
}
Я использовал график отладочной памяти, чтобы выяснить, что все еще хранит ссылку на экземпляр MyClass, и вот что я получаю:
Почему до сих пор есть ссылка? Что мне здесь не хватает?
ОБНОВИТЬ
Поэтому я пытался выяснить, почему он работает с MyViewController, но не при наличии другого экземпляра между ними. Кажется, что наследование от NSObject имеет значение. Когда я наследую MyClass от NSObject, вызывается deinit, и как только длинная операция завершается, для self тогда правильно устанавливается значение nil.
Теперь возникает вопрос, каким образом списки захвата в Swift связаны с NSObject?