Должна ли асинхронная функция Swift сохранять сильную ссылку на объект?

Мы реализовали расширение для NSData, которое асинхронно сохраняет данные в URL. Вот краткая версия функции.

extension NSData {

    func writeToURL1(url:NSURL, completion: () -> Void)  {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { [weak self] in

            guard let strongSelf = self else { return }

            strongSelf.writeToURL(url, atomically: true)

            completion()
            })
    }
}

Вот как мы это используем:

var imageData = UIImageJPEGRepresentation(image, 0.8)
imageData?.writeToURL(someURL) { in ... }

Проблема, конечно, в том, что если imageData будет освобожден до завершения операции записи, strongSelf будет равно нулю, и обработчик завершения никогда не будет вызван.

Есть два решения этой проблемы.

  1. удалить [weak self] (т.е. иметь сильную ссылку на self в writeToURL1.
  2. ссылка imageData в блоке завершения (т.е. imageData?.writeToURL(someURL) { in imageData = nil ... })

Какой подход более дружелюбен к Swift и какой подход нам следует выбрать?

Благодарю вас!


person Sei Flavius    schedule 13.09.2016    source источник


Ответы (2)


Вы должны использовать сильную ссылку, если вы не хотите, чтобы объект ушел из-под ваших ног (и это не создаст цикл ссылок); слабая ссылка, если вам все равно, если предмет уйдет из-под ваших ног; бесхозная ссылка, если вам нужно, чтобы объект был рядом, и у вас есть сильная гарантия, что объект не уйдет из-под ваших ног, но сильная ссылка создаст цикл.

В вашем случае вы заботитесь о том, чтобы объект мог исчезнуть, потому что это означает, что вы не сможете его сохранить. Поскольку у вас нет гарантии, что что-то еще будет поддерживать объект в рабочем состоянии до тех пор, пока эта задача не будет завершена, вы должны использовать сильную ссылку.

Сильные ссылки на self в замыканиях являются проблемой, когда вы носите замыкание с собой, потому что легко получить неочевидный цикл ссылок, но вы точно знаете, что ссылка будет удалена сразу после выполнения замыкания, поэтому здесь не проблема.

person zneak    schedule 13.09.2016
comment
Спасибо. Ваш ответ имеет смысл, и мы, скорее всего, выберем вариант 1. Причина, по которой мы стали слабыми, заключается в том, что мы думали об использовании этого приложения в масштабе приложения и хотели, чтобы следующий разработчик выбрал, сохранять ли его в памяти или нет, а не просто отправить его прочь без понятия. Речь шла о том, кто будет контролировать память, человек, вызывающий функцию, или сам процесс. - person Sei Flavius; 13.09.2016

Похоже, вы просто хотите строго зафиксировать ссылку на объект NSData, поэтому удаление [weak self] — это, на мой взгляд, самый простой и лучший подход. Обычно слабые ссылки используются, чтобы избежать циклов сохранения при захвате в замыкании. Однако на самом деле вы не создаете цикл сохранения, а просто сохраняете в одну сторону, захватывая себя в блоке. Блок не сохраняется сам, он просто передается по стеку вызовов в dispatch_async, где он в конечном итоге вызывается и освобождается. Таким образом, при использовании weak self нет цикла сохранения, которого можно было бы избежать, просто сохранение происходит через закрытие, что желательно. То есть вы хотите хранить данные в памяти до тех пор, пока не будет вызвано это закрытие.

person Patrick Goley    schedule 13.09.2016