Вы говорите:
Исходя из того, что я понимаю, здесь есть установка:
self ---> queue
self <--- block
Очередь - это просто оболочка / оболочка для блока.Вот почему, даже если я nil
очереди, блок продолжит свое выполнение.Они независимы.
Тот факт, что self
имеет сильную ссылку на очередь, несущественен.Лучший способ думать об этом - это то, что сам GCD хранит ссылку на все очереди отправки, в которых есть что-либо в очереди.(Это аналог пользовательского экземпляра URLSession
, который не будет освобожден до тех пор, пока не будут выполнены все задачи в этом сеансе.)
Таким образом, GCD сохраняет ссылку на очередь с отправленными задачами.Очередь сохраняет строгую ссылку на отправленные блоки / элементы.Блок в очереди сохраняет строгую ссылку на любые ссылочные типы, которые они захватывают.Когда отправленная задача завершается, она разрешает любые сильные ссылки на любые захваченные ссылочные типы и удаляется из очереди (если вы не сохраняете свою собственную ссылку на нее в другом месте.), Обычно таким образом разрешая любые циклы сильных ссылок.
Если не учитывать, что отсутствие [weak self]
может привести к неприятностям, GCD по какой-то причине сохраняет ссылку на блок, например, на источники отправки.Классическим примером является повторяющийся таймер:
class Ticker {
private var timer: DispatchSourceTimer?
func startTicker() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".ticker")
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: 1)
timer!.setEventHandler { // whoops; missing `[weak self]`
self.tick()
}
timer!.resume()
}
func tick() { ... }
}
Даже если контроллер представления, в котором я запустил вышеупомянутый таймер, отключается, GCD продолжает активировать этот таймер, и Ticker
не будет выпущен.Как показывает функция «Диаграмма отладочной памяти», блок, созданный в подпрограмме startTicker
, постоянно сохраняет сильную ссылку на объект Ticker
:
Это, очевидно, решается, если я использую [weak self]
в этом блоке, используемом в качестве обработчика событий для таймера, запланированного в этой очереди отправки.
Другие сценарии включают отправку медленной (или неопределенной длины) задачигде вы хотите cancel
это (например, в deinit
):
class Calculator {
private var item: DispatchWorkItem!
deinit {
item?.cancel()
item = nil
}
func startCalculation() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".calcs")
item = DispatchWorkItem { // whoops; missing `[weak self]`
while true {
if self.item?.isCancelled ?? true { break }
self.calculateNextDataPoint()
}
self.item = nil
}
queue.async(execute: item)
}
func calculateNextDataPoint() {
// some intense calculation here
}
}
Все это было сказано,в подавляющем большинстве случаев использования GCD выбор [weak self]
- это не один из сильных эталонных циклов, а скорее просто вопрос о том, будем ли мы возражать, если сильная ссылка на self
сохраняется до тех пор, пока задача не будет выполнена или нет.
Если мы просто собираемся обновить пользовательский интерфейс после выполнения задачи, нет необходимости держать контроллер представления и его представления в иерархии в ожидании некоторого обновления пользовательского интерфейса, если представлениеконтроллер был уволен.
Если нам нужно обновить хранилище данных после выполнения задачи, то мы определенно не хотим использовать [weak self]
, если мы хотим убедиться, что обновление произойдет.
Часто отправленные задачи не настолько важны, чтобы беспокоиться о сроке службы self
.Например, у вас может быть обновление пользовательского интерфейса отправки обработчика завершения URLSession
обратно в основную очередь после выполнения запроса.Конечно, мы теоретически хотели бы [weak self]
(поскольку нет причин сохранять иерархию представления для контроллера представления, который был отклонен), но опять же, это добавляет шум к нашему коду, часто с небольшим материальным преимуществом.
Не имеет значения, но игровые площадки - ужасное место для проверки поведения памяти, потому что у них есть свои особенности.Гораздо лучше сделать это в реальном приложении.Кроме того, в реальном приложении у вас есть функция «Диаграмма отладочной памяти», где вы можете увидеть реальные сильные ссылки.См https://stackoverflow.com/a/30993476/1271826.