Как предотвратить освобождение объектов в GCD?
Эта функция должна перепланировать выполнение рабочего элемента:
class MyClass {
var id: String?
var workItem: DispatchWorkItem?
var isDoing = false
func myFunction() {
guard let id = self.id else { return }
isDoing = true
NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
workItem?.cancel()
workItem = DispatchWorkItem {
self.isDoing = false
NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
}
if let workItem = workItem {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(10), execute: workItem)
}
}
}
Он отлично работает в разработке, но дизайн кажется подозрительным, поэтому я задаю несколько основных вопросов:
- Мог
workItem
быть ноль, еслиworkItem?.cancel()
вызывается непосредственно перед тем, как очередь пытается выполнитьworkItem
? - Мог
id
внутриworkItem
когда-нибудь будет ноль, когдаworkItem
выполняется или удерживается областью действияlet id = self.id
? - Мог
isDoing
внутриworkItem
быть уже освобождены, когдаworkItem
выполняется, еслиMyClass
объект был освобожден? Другими словами, что происходит с запланированнымworkItem
когдаMyClass
объект освобожден?
1 ответ
Не уверен, что вы имеете в виду. Вы не ноль
workItem
в любом месте.Нет не может быть
nil
так как вы работаете с локальной переменной - копияself.id
, Используяguard
вы убедитесь, что локальная переменнаяid
не ноль, и закрытие сохраняет строгую ссылку (по умолчанию) на захваченные значения, поэтому оно не будет освобождено.isDoing
переменная экземпляраMyClass
поэтому он не может быть освобожден раньшеMyClass
Экземпляр освобожден. Проблема в вашем случаеMyClass
не может быть освобожден, потому что вы смотрите на сильный ссылочный цикл. Закрытие по умолчанию сохраняет строгую ссылку на захваченное значение, и вы захватываетеself
, И с тех порself
держит сильную ссылку наworkItem
который в свою очередь держит сильную ссылку на закрытие, которое захватываетself
отсюда и эталонный цикл.
В общем при захвате self
вы используете список захвата для работы со слабой ссылкой на self
и сделайте проверку, не было ли это освобождено
workItem = DispatchWorkItem { [weak self] in
guard let strongSelf = self else { return }
// do stuff with strongSelf
}