Как предотвратить освобождение объектов в GCD? - PullRequest
0 голосов
/ 25 июня 2018

Предполагается, что эта функция позволяет запланировать выполнение рабочего элемента:

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 Ответ

0 голосов
/ 25 июня 2018
  1. Не уверен, что вы имеете в виду.Вы нигде не пропустите workItem.

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

  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
}
...