Утечка памяти в структурах Swift. Как это исправить?
Я разрабатываю приложение в Swift 2 (Xcode 7 beta 3), и, когда это возможно, я пытаюсь использовать типы значений (структуры и перечисления). Согласно документации Apple относительно управления памятью, работа с типами значений не должна вызывать каких-либо циклов сохранения, и она должна просто работать.
Но сегодня я столкнулся с огромным количеством утечек памяти в коде обработки событий. Я отследил его и уменьшил проблему до следующего минимального примера.
Скажем, существует протокол Item
, который определяет одно свойство value
:
protocol Item {
var value: String { get }
}
Затем мы создаем конкретную структуру, которая реализует протокол Item
и добавляет дополнительное свойство additionalValue
. Позвольте называть struct FooItem
.
struct FooItem<T>: Item {
let value: String
let additionalValue: T
init(value: String, additionalValue: T) {
self.value = value
self.additionalValue = additionalValue
}
}
Третья часть головоломки - это другая структура, которая обертывает элемент, реализующий протокол Item
. Он называется ItemWrapper
.
struct ItemWrapper {
let item: Item
init(item: Item) {
self.item = item
}
}
Если в приложении "Инструменты" используется конфигурация "Утечки памяти", утечка памяти появляется каждый раз, когда значение ItemWrapper
создается с помощью FooItem
.
let item = FooItem(value: "protocol value", additionalValue: "foo item value")
let _ = ItemWrapper(item: item)
![Instruments screenshot 1]()
![Instruments screenshot 2]()
Вот пример проекта Xcode и файла Инструменты: https://www.dropbox.com/s/z6ugxzxqggrv1xl/SwiftStructsMemoryLeak.zip?dl=0
Пример всего кода можно просмотреть в этом Gist: https://gist.github.com/lukaskubanek/4e3f7657864103d79e3a
Вот отчет об ошибке: rdar://21375421
Это ошибка в компиляторе Swift или я делаю что-то неправильно?
РЕДАКТИРОВАТЬ 1. Как было предложено в комментариях, я перепробовал этот вопрос на форуме Apple Dev Forum, чтобы привлечь больше внимания со стороны сообщества Swift и потенциально от разработчиков языка. Из-за миграции форумов dev во время WWDC 2015 я должен был опубликовать обновленный вопрос на новых форумах. Вот ссылка: https://forums.developer.apple.com/message/9643
РЕДАКТИРОВАТЬ 2. Проблема, которую я первоначально разместил в примере кода, кажется, разрешена в Swift 2.0. Поскольку это не решило проблемы в моем приложении, я сделал еще одну модификацию кода примера. Теперь дополнительное свойство FooItem
имеет общий тип, а FooItem
аннотируется с типом и, следовательно, общим типом. Вот как я использую его в своем приложении, и он все еще вызывает утечку памяти, но на этот раз, когда ItemWrapper
инициализируется, а не при доступе к свойству.
РЕДАКТИРОВАТЬ 3. Полностью обновите вопрос к модифицированной проблеме, которая сохраняется в Swift 2.0 и загружена в новый проект Xcode.
Ответы
Ответ 1
Хотя у меня нет ответа от Apple ни на форумах разработчиков, ни в отслеживании ошибок, и я не нашел ничего связанного с этой проблемой в примечаниях к выпуску последних бета-версий, это, кажется, решено в компилятор Swift в Xcode 7 beta 5. (Возможно, он также работает в бета-версии 4. Последняя версия, которую я проверил, была бета-3.)
Демонстрационный проект больше не создает утечку памяти. То же самое верно для моего приложения. Ура!
![введите описание изображения здесь]()
Ответ 2
Ну, вот обходной путь, хотя я понятия не имею, почему он работает. Я заметил, что если вы это сделаете:
let theItem = itemWrapper.item
let value = theItem.value
... вместо этого:
let value = itemWrapper.item.value
... он не создает утечку памяти.