Утечка памяти в структурах Swift — как это исправить?

Я разрабатываю приложение в Swift 2 (Xcode 7 beta 3) и пытаюсь использовать типы значений (структуры и перечисления), где это возможно. Согласно документации Apple об управлении памятью, работа с типами значений не должна вызывать циклов сохранения и должна просто работать.

Но сегодня я столкнулся с огромным количеством утечек памяти в коде обработки событий. Я отследил это и свел проблему к следующему минимальному примеру.

Допустим, есть протокол Item, определяющий одно свойство value:

protocol Item {

    var value: String { get }

}

Затем мы создаем конкретную структуру, которая реализует протокол Item и добавляет дополнительное свойство additionalValue. Назовем структуру 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) 

Скриншот инструментов 1Скриншот инструментов 2

Вот пример проекта Xcode и файл инструментов: https://www.dropbox.com/s/z6ugxzxqggrv1xl/SwiftStructsMemoryLeak.zip?dl=0

Пример всего кода можно просмотреть в этом Gist: https://gist.github.com/lukaskubanek/4e3f7657864103d79e3a

Вот отчет об ошибке: rdar://21375421

Это ошибка компилятора Swift или я что-то не так делаю?


РЕДАКТИРОВАТЬ 1. Как было предложено в комментариях, я повторно разместил этот вопрос на Форуме разработчиков Apple, чтобы привлечь больше внимания сообщества Swift и, возможно, разработчиков языка. . Из-за миграции форумов разработчиков во время WWDC 2015 мне пришлось опубликовать обновленный вопрос на новых форумах. Вот ссылка: https://forums.developer.apple.com/message/9643


РЕДАКТИРОВАНИЕ 2: проблема, которую я первоначально описал в примере кода, похоже, решена в Swift 2.0. Поскольку это не решило проблемы в моем приложении, я внес еще одну модификацию в код примера. Теперь дополнительное свойство FooItem имеет универсальный тип, а FooItem аннотируется типом и, следовательно, универсальным типом. Вот как я использую его в своем приложении, и он по-прежнему вызывает утечку памяти, но на этот раз при инициализации ItemWrapper, а не при доступе к свойству.


РЕДАКТИРОВАТЬ 3: Полностью обновлен вопрос для измененной проблемы, которая сохраняется в Swift 2.0, и загружен новый пример проекта Xcode.


person Lukas Kubanek    schedule 03.06.2015    source источник
comment
Что вы имеете в виду под словом «в другом месте» — в коде или для получения дополнительных ресурсов по теме на других веб-сайтах?   -  person Lukas Kubanek    schedule 03.06.2015
comment
Пример на самом деле состоит только из этих трех объектов и кода, который их вызывает. См. этот Gist gist.github.com/lukaskubanek/4e3f7657864103d79e3a для всего AppDelegate. Извините, что не упомянул об этом в вопросе.   -  person Lukas Kubanek    schedule 03.06.2015
comment
Я смог воспроизвести проблему, но понятия не имею, что, черт возьми, происходит .. +1   -  person Mazyod    schedule 03.06.2015
comment
Приятно слышать, что это не только я. Так ты думаешь, что это баг? Должен ли я написать радар? Возможно, на следующей неделе мы получим новую версию Swift, которая может решить эту проблему...   -  person Lukas Kubanek    schedule 03.06.2015
comment
Интересно, что это происходит только с оберткой, и если вы добавите больше оберток вокруг обертки, хуже не станет. Это может быть ошибка. Возможно, вы захотите поднять это на форумах разработчиков и посмотреть, считают ли люди это ошибкой. Это довольно интересно, поэтому, вероятно, быстро привлечет внимание.   -  person Ben Kane    schedule 03.06.2015
comment
@BenKane Спасибо за подсказку! Я повторно разместил вопрос на форуме разработчиков Swift. Посмотрим, как получится.   -  person Lukas Kubanek    schedule 03.06.2015
comment
@LukasKubanek Круто, я слежу за этим там   -  person Ben Kane    schedule 03.06.2015


Ответы (2)


Хотя я не получил ответа от Apple ни на форумах разработчиков, ни в системе отслеживания ошибок, и я не нашел ничего, связанного с этой проблемой в примечаниях к выпуску последних бета-версий, похоже, она решена в компиляторе Swift. в Xcode 7 бета 5. (Возможно, это также работает в бета-версии 4. Последней версией, которую я проверял, была бета-версия 3.)

Демонстрационный проект больше не вызывает утечки памяти. То же самое верно и для моего приложения. Ура!

введите здесь описание изображения

person Lukas Kubanek    schedule 11.08.2015

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

    let theItem = itemWrapper.item
    let value = theItem.value

...вместо этого:

    let value = itemWrapper.item.value

... это не приводит к утечке памяти.

person cc.    schedule 04.06.2015
comment
Я тоже нашел этот обходной путь. Проблема в том, что это работает только для свойств. В моем реальном коде у меня также есть методы, определенные в протоколе, и они также генерируют утечки памяти. Поэтому мне пришлось писать функции, обертывающие все конкретные типы (например, FooItem и т. д.), и это неинтересно. - person Lukas Kubanek; 05.06.2015
comment
Ага. Я предполагаю, что это просто ошибка в компиляторе Swift, которая (надеюсь) в конечном итоге будет исправлена, и решение состоит в том, чтобы просто опубликовать радар и подождать, пока Apple ее исправит. (Существует есть директива компилятора, которая позволяет вам включить оптимизацию Swift, которая даст вам другую сборку. Вы можете попробовать включить ее и посмотреть, исправит ли это ее. Но имейте в виду, что это также приводит к некоторым известным проблемам. , так что это может быть компромиссом.) - person cc.; 05.06.2015
comment
Хорошо, я подожду сегодняшних анонсов (WWDC) и подам радар, если этот баг не будет исправлен в новой версии Swift. - person Lukas Kubanek; 08.06.2015
comment
Через год у меня была такая же ситуация. Этот обходной путь решил мою проблему с утечкой памяти. Проблема в том, что я тоже не понимаю, почему это решение работает. Если это ошибка в Swift, я думаю, что она будет очень распространенной, и к настоящему времени ее следует устранить... - person Rados; 31.08.2016