Как предотвратить освобождение объектов в 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)
        }
    }
}

Он отлично работает в разработке, но дизайн кажется подозрительным, поэтому я задаю несколько основных вопросов:

  1. Мог workItem быть ноль, если workItem?.cancel() вызывается непосредственно перед тем, как очередь пытается выполнить workItem?
  2. Мог id внутри workItem когда-нибудь будет ноль, когда workItem выполняется или удерживается областью действия let id = self.id?
  3. Мог isDoing внутри workItem быть уже освобождены, когда workItem выполняется, если MyClass объект был освобожден? Другими словами, что происходит с запланированным workItem когда MyClass объект освобожден?

1 ответ

Решение
  1. Не уверен, что вы имеете в виду. Вы не ноль workItem в любом месте.

  2. Нет не может быть nil так как вы работаете с локальной переменной - копия self.id, Используя guard вы убедитесь, что локальная переменная id не ноль, и закрытие сохраняет строгую ссылку (по умолчанию) на захваченные значения, поэтому оно не будет освобождено.

  3. 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
}
Другие вопросы по тегам