Утечки памяти ARC

Я испытываю утечки памяти, связанные с NSMutableArray в проекте, настроенном на использование ARC, который, как я думал, должен был справиться с этими вещами для вас.

Следующий код вызывает утечку NSNumbers:

NSMutableArray *myArray = [[NSMutableArray alloc] init];

NSNumber  *myNumber = [NSNumber numberWithFloat:10];

[myArray addObject:myNumber];

Запуск последней строки дает в отладчике следующее:

objc[1106]: объект 0x765ffe0 класса __NSCFNumber автоматически выпущен без пула — просто утечка — прерывание objc_autoreleaseNoPool() для отладки

Кроме того, объект правильно добавлен в изменяемый массив,

Я делаю что-то очевидное неправильно?

Примечание. В проекте есть один класс, который мне не удалось заставить работать с ARC, поэтому я исключил его из ARC с помощью флага компилятора -fno-objc-arc. Однако утечки происходят и в других классах, использующих ARC. Не уверен, что это связано.

Большое спасибо за вашу помощь.


person Spinoxa    schedule 19.03.2012    source источник
comment
Этот код выполняется в отдельном потоке? Или в основном методе вне контекста @autoreleasepool?   -  person Richard J. Ross III    schedule 19.03.2012
comment
Единственное упоминание @autoreleasepool в проекте есть в main.m. Код неисправности находится в других классах. Как я могу проверить, находится ли метод в отдельном потоке? Я сознательно не помещал это в отдельную ветку, но возможно, что это произошло. Я построил проект на основе некоторого неофициального образца кода, используя аудиоустройства, которые я нашел в Интернете, поэтому я не уверен во всех элементах.   -  person Spinoxa    schedule 19.03.2012
comment
Привет Ричард, Утечки происходят в потоке, отдельном от основного, в том же потоке, который выполняет рендеринг Audio Unit, который является классом, исключенным из дуги. Есть ли способ перенаправить процесс в основной поток? Или вы думаете, что мне следует еще раз попытаться заставить рендеринг Audio Unit работать с ARC? Большое спасибо за вашу помощь, я думаю, вы указали вероятную причину!   -  person Spinoxa    schedule 19.03.2012
comment
Я полагаю, что другим путем, который я мог бы выбрать, было бы также исключить проблемные классы из дуги, используя флаг компилятора, и делать все по старинке. Что вы порекомендуете? Я неопытен, поэтому хотел использовать ARC, если это возможно...   -  person Spinoxa    schedule 19.03.2012
comment
Просто добавьте @autoreleasepool, когда поток начнет выполняться, и все будет в порядке...   -  person Richard J. Ross III    schedule 19.03.2012
comment
Как я могу найти точку, где новый поток начинает выполняться? Как это может выглядеть? Первое, что я вижу упомянутым в потоке, это AUIOHelper::NotifyInputAvailable, который, как я предполагаю, является закулисным компонентом метода рендеринга аудиоустройства, который имеет тип OSStatus. Где я могу добавить @autoreleasepool? Большое спасибо.   -  person Spinoxa    schedule 19.03.2012
comment
Просто добавьте пул вокруг всех ваших методов, которые выполняются в другом потоке.   -  person Richard J. Ross III    schedule 19.03.2012
comment
Сделал это! Добавлен @autoreleasepool в качестве первой строки в классе OSStatus, и это остановило утечки!! Большое спасибо.   -  person Spinoxa    schedule 19.03.2012


Ответы (2)


Вероятно, вы запускаете этот код в фоновом потоке и у вас нет пула автоматического выпуска. ARC по-прежнему будет иногда автоматически освобождать объекты для вас, и если вы обращаетесь к платформам Apple, они все еще могут быть не ARC, поэтому они определенно могут автоматически освобождать объекты для вас. Таким образом, вам все еще нужен пул автоматического выпуска.

Cocoa создает для вас пул автоматического освобождения в основном потоке, но ничего не делает для вас в фоновых потоках. Если вы собираетесь запустить что-то в фоновом потоке без использования NSOperation или чего-то подобного, вам нужно обернуть этот поток в @autoreleasepool, например так:

- (void)doSomething {
    [self performSelectorInBackground:@selector(backgroundSomething)];
}

- (void)backgroundSomething {
    @autoreleasepool {
        NSLog(@"Here I am in the background, doing something.");
        myArray = [[NSMutableArray alloc] init];
        // etc.
    }
}
person BJ Homer    schedule 26.03.2012
comment
Спасибо, это действительно была проблема! - person Spinoxa; 27.03.2012
comment
Отлично, спасибо. Инструмент Instruments Leak не находит их, так как я думаю, что технически они не являются утечками. - person n13; 20.10.2012
comment
Немного запутанное заявление о том, что Cocoa автоматически создает пул авторелиза, я считаю, что это работает, потому что шаблон main.m имеет @autoreleasepool { в int main(. - person A-Live; 09.04.2013
comment
Что ж, в Cocoa также есть отдельный пул авторелиза, который создается для каждого прохода основного цикла выполнения потока. - person BJ Homer; 09.04.2013
comment
@BJHomer спасибо за ответ, который мне помог, однако, если - (void)backgroundSomething также состоит из цикла for, скажем, 500 прогонов, я должен использовать как - (void)backgroundSomething{ for(int i 500 run){ @autorelease{... нслог(и)}}}... - person maddy; 13.11.2013
comment
@Maddy Если вас беспокоит накопление памяти внутри цикла for, может подойти пул автоматического освобождения внутри. По-прежнему разумно иметь его на корневом уровне, просто чтобы избежать утечек. Вы можете вложить в него дополнительные пулы автоматического выпуска, если хотите. - person BJ Homer; 13.11.2013
comment
что я могу сделать в такой ситуации, как этот вопрос stringwithutf8string с включенной дугой"> stackoverflow.com/questions/21423309/ - person deltaaruna; 05.02.2014
comment
Я не понимаю, если идея состоит в том, чтобы просто спамить \@autoreleasepool везде, то почему компилятор этого не делает? Если компилятор не делает это автоматически для каждого потока, должна же быть причина не помещать туда \@autoreleasepool иногда, верно? Когда следует или не следует помещать \@autoreleasepool в поток? - person Kevin; 08.12.2014
comment
@Kevin, вам нужен пул автоматического выпуска, если поток будет выполнять код Objective-C и еще не предоставляет свои собственные пулы. Пул, который завершает выполнение всего потока, не будет стекать до тех пор, пока весь поток не завершится; для длительных потоков это было бы не идеально. Например, было бы лучше поместить его в цикл внутри тела цикла, чтобы он периодически сбрасывался. За размещение пула отвечает программист. - person BJ Homer; 27.12.2014

Скорее всего, вы определили NSMutableArray как статическую переменную. Когда вы делаете это, вы выходите за пределы любого пула автовыпуска, поскольку статические определения активируются вне любого цикла выполнения. ARC не волшебный, он просто автоматизирует вызовы управления памятью в рамках существующей структуры сохранения/освобождения и поэтому не может помочь в таких случаях.

Решение состоит в том, чтобы инициализировать статическую переменную где-нибудь в классе, чтобы ваш изменяемый массив был построен в цикле выполнения.

person Kendall Helmstetter Gelner    schedule 19.03.2012
comment
Нет, статические переменные не вызывают утечек памяти, если бы они были, то у меня их было бы много в моем текущем проекте, я считаю, что он просто выполняет код вне контекста @autoreleasepool. - person Richard J. Ross III; 19.03.2012
comment
Как бы это выглядело, если бы я определил NSMutableArray как статическую переменную? Должен ли массив быть объявлен в заголовочном файле класса и с какими свойствами? Спасибо - person Spinoxa; 19.03.2012
comment
Это не утечка памяти. По крайней мере, не так, как это определяет большинство людей. Практически единственный способ, с которым я столкнулся на практике, для выполнения кода вне контекста авторелиза, - это объявить NSObject в статической переменной - один случай, который я знаю, вызывает это точное сообщение, объявляя статический UIImage с imageNamed в глобальной статической переменной. - person Kendall Helmstetter Gelner; 19.03.2012
comment
@Spin: это будет выглядеть как статический NSMutableArray *array = [[NSMutableArray alloc] init], но он будет находиться за пределами блока реализации класса. На самом деле, даже если бы он не был статическим, а просто переменной, объявленной вне реализации как глобальная, это могло бы вызвать ту же проблему. Я видел ваше ТОЧНОЕ сообщение об ошибке раньше, поэтому я уверен, что это ваша проблема ... когда вы найдете объявление и исправите его, пожалуйста, отметьте мой ответ как правильный, поскольку будет очень забавно, если отрицательный ответ будет отмечен как правильный. - person Kendall Helmstetter Gelner; 19.03.2012
comment
Привет, Кендалл, спасибо за ваши предложения, но ответ Ричарда решил проблему: необходимо настроить пул авторелиза вокруг вторичного потока. - person Spinoxa; 21.03.2012
comment
Какое отношение runloop имеет к ARC? Кроме того, этот ответ вообще не отвечает на вопрос. Это объект, который автоматически освобождается без пула, как указано в ошибке. - person mattjgalloway; 27.03.2012
comment
Он представляет собой ответ на то, что могло быть проблемой, которую я видел сам. Даже с ARC, если вы создадите статическую переменную UIImage вне какого-либо класса и назначите ей экземпляр с imageNamed, вы увидите точно такое же сообщение. Я оставил (очевидно, презираемый) ответ на месте, чтобы будущие пользователи Google могли узнать, что происходит не так. В конце концов кто-нибудь поблагодарит меня. - person Kendall Helmstetter Gelner; 27.03.2012
comment
Потому что люди злые и невежественные. - person Kendall Helmstetter Gelner; 14.02.2014