Обязательно ли использовать autoreleasepool в программе Swift?

97

На странице 17 этой презентации WWDC14 говорится:

Работаете с Objective-C? По-прежнему нужно управлять автоматическим выпуском пулов
autoreleasepool {/ * code * /}

Что это значит? Означает ли это, что если в моей кодовой базе нет файлов Objective-C, в autoreleasepool {}этом нет необходимости?

В ответе на связанный вопрос есть пример, где autoreleasepoolможет быть полезно:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

Если приведенный выше код будет переведен на Swift с помощью autoreleasepooldrop, будет ли Swift достаточно умен, чтобы знать, что numberпеременная должна быть выпущена после первой }(как это делают некоторые другие языки)?

Итан
источник
1
Похоже, что autoreleasepoolв Swift нет документации . Я расширил ваш вопрос и задал его на форумах разработчиков .
Аарон Брагер

Ответы:

199

autoreleasepoolШаблон используется в Swift при возвращении autoreleaseобъектов (созданных либо кодом Objective-C или с помощью классов какао). autoreleaseШаблон в функции Swift много , как это делает в Objective-C. Например, рассмотрим эту Swift-версию вашего метода (создание экземпляров NSImage/ UIImageобъектов):

func useManyImages() {
    let filename = pathForResourceInBundle

    for _ in 0 ..< 5 {
        autoreleasepool {
            for _ in 0 ..< 1000 {
                let image = NSImage(contentsOfFile: filename)
            }
        }
    }
}

Если вы запустите это в инструментах, вы увидите график распределения, подобный следующему:

с автоматическим выпуском пула

Но если вы сделаете это без пула автозапуска, вы увидите, что пиковое использование памяти выше:

без автозапуска

autoreleasepoolПозволяет явно управлять , когда autorelease объекты высвобождены в Swift, так же , как вы были в состоянии в Objective-C.

Примечание. При работе с собственными объектами Swift вы обычно не получаете объекты с автоматическим выпуском. Вот почему в презентации упоминалось предостережение о необходимости этого только при «работе с Objective-C», хотя я бы хотел, чтобы Apple была более ясной по этому поводу. Но если вы имеете дело с объектами Objective-C (включая классы Какао), они могут быть объектами с автоматическим выпуском, и в этом случае это представление Swift @autoreleasepoolшаблона Objective-C по-прежнему полезно.

Роб
источник
2
На все эти вопросы, вы можете написать свой собственный класс, и он делать printlnпо прибытии deinit, и это становится довольно легко проверить точно , когда объекты освобождаться. Или наблюдайте в инструментах. Отвечая на ваш вопрос, похоже, что объекты Swift возвращаются из функций с +1 счетчиком сохранения (не для объектов с автоматическим выпуском), и вызывающая сторона будет легко управлять владением с этой точки (например, если и когда возвращенный объект выпадает из области видимости, он немедленно освобождается, а не помещается в пул автозапуска).
Роб
3
@StevenHernandez Пул с автоматическим выпуском имеет мало общего с утечками. Утечка вызвана невыпущенным объектом. С другой стороны, пул автозапуска - это просто набор объектов, выпуск которых откладывается до тех пор, пока пул не будет опустошен. Пулы не контролируют, будет ли что-то освобождено или нет, а скорее просто время такого освобождения. При повторном представлении карты вы не можете контролировать, какое кеширование оно выполняет (использует память, но не истинную утечку), и ничего не делать, если произошла настоящая утечка (и я не знаю каких-либо значительных утечек на карте, хотя исторически были случайные, скромные утечки в UIKit).
Роб
2
@matt Да, я видел подобное. Поэтому я повторил свое упражнение с NSImage/ UIImageobjects и более последовательно проявил проблему (и, честно говоря, это более частый пример проблемы, поскольку пиковое использование памяти часто проблематично только при работе с более крупными объектами; практическим примером этого может быть рутинное изменение размера кучи изображений). Я также воспроизвел поведение, вызывающее код Objective-C, который явно создает объекты автозапуска. Не поймите меня неправильно: я думаю, что пулы с автоматическим выпуском в Swift нужны реже, чем в Objective-C, но это все еще играет роль.
Роб
1
Я нашел пример, который работает! Просто звоните в NSBundle несколько pathForResource:ofType:раз.
Мэтт
1
Мой pathForResource:ofType:пример больше не работает в Xcode 6.3 / Swift 1.2. :)
Мэтт
5

Если бы вы использовали его в эквивалентном коде Objective-C, вы бы использовали его в Swift.

будет ли Swift достаточно умен, чтобы знать, что числовая переменная должна быть выпущена после первой}

Только если Objective-C это сделает. Оба работают согласно правилам управления памятью Какао.

Конечно, ARC знает, что numberвыходит за рамки в конце этой итерации цикла, и, если он сохранил это, он освободит его там. Однако это не говорит вам, был ли объект выпущен автоматически, потому что -[NSNumber numberWithInt:] мог или не мог вернуть автоматически выпущенный экземпляр. Вы не можете узнать это, потому что у вас нет доступа к источнику -[NSNumber numberWithInt:].

newacct
источник
1
Если Swift для этого ведет себя так же, как Objective-C, почему в презентации упоминается «Работа с Objective-C»? конкретно?
Итан
9
@Ethan Похоже, что собственные объекты Swift не являются объектами с автоматическим выпуском, и эта autoreleasepoolконструкция совершенно не нужна. Но если ваш код Swift обрабатывает объекты Objective-C (включая объекты Какао), они действительно следуют шаблонам автозапуска, и, таким образом, autoreleasepoolконструкция становится полезной.
Роб
Я понимаю, что «autoreleasepool позволяет вам явно управлять освобождением объектов autorelease в Swift», но зачем мне это делать? Почему компилятор не делает это за меня? Мне пришлось добавить свой собственный autoreleasepool, чтобы виртуальная машина не проскочила через крышу в жестком цикле огромных манипуляций со строками. Для меня было очевидно, куда это нужно добавить, и все работало отлично. Почему компилятор не смог этого сделать? Можно ли сделать компилятор умнее, чтобы он хорошо с этим справлялся?
vonlost