Утечка памяти с помощью [NSKeyedUnarchiver decodeObjectForKey]
-
05-07-2019 - |
Вопрос
Каждый раз, когда я вызываю этот метод, мои NSmutableData просачиваются, и я не могу понять, как их подключить.Счетчик сохранения данных увеличивается на единицу после выделения и инициализации декодера, и я понятия не имею, почему.Я застрял со счетчиком сохранения, равным 2, в конце метода, и попытка освободить его приводит к сбою приложения.
- (void)readVenueArchiveFile:(NSString *)inFile key:(NSString *)inKey
{
NSMutableData *theData;
NSKeyedUnarchiver *decoder;
theData = [NSData dataWithContentsOfFile:inFile];
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
venueIOList = [[decoder decodeObjectForKey:inKey] mutableCopy];
[decoder finishDecoding];
[decoder release];
}
Решение
Я бы предложил заменить эту строку:
venueIOList = [[decoder decodeObjectForKey:inKey] mutableCopy];
с:
ListClassName *decodedList = [decoder decodeObjectForKey:inKey];
self.venueIOList = decodedList;
Это делает управление памятью decodedList
прозрачный.Считается лучшей практикой присваивать переменные экземпляра с помощью метода доступа (за исключением методов инициализации).В вашей текущей реализации, если вы когда-нибудь вызовете readVenueArchiveFile:
второй раз на одном и том же объекте вы воля утечка (как и если бы decodedList
уже имеет значение).Более того, вы можете поместить логику копирования в свой метод доступа и забыть о ней, вместо того, чтобы запоминать mutableCopy каждый раз, когда вы назначаете новое значение (при условии, что все равно есть веская причина для создания изменяемой копии?).
Другие советы
Уменьшение пикового потребления памяти
В общем, рекомендуется избегать создания автоматически выпускаемых объектов.
[Большая часть этого параграфа изменена с этот вопрос.] Поскольку вы обычно (1) не имеете прямого контроля над их временем жизни, автоматически выпускаемые объекты могут сохраняться сравнительно долго и неоправданно увеличивать объем памяти вашего приложения.На рабочем столе это может не имеет большого значения, на более ограниченных платформах это может стать серьезной проблемой.Поэтому на всех платформах, и особенно на платформах с более ограниченными возможностями, вам настоятельно не рекомендуется использовать методы, которые могут привести к автоматическому освобождению объектов, и вместо этого рекомендуется использовать шаблон alloc/init.
Я бы предложил заменить это:
theData = [NSData dataWithContentsOfFile:inFile];
с:
theData = [[NSData alloc] initWithContentsOfFile:inFile];
затем в конце метода добавьте:
[theData release];
Это значит, что theData
будет освобожден перед выходом из метода.В итоге у вас должно получиться:
- (void)readVenueArchiveFile:(NSString *)inFile key:(NSString *)inKey
{
NSMutableData *theData;
NSKeyedUnarchiver *decoder;
theData = [[NSData alloc] initWithContentsOfFile:inFile];
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
ListClassName *decodedList = [decoder decodeObjectForKey:inKey];
self.venueIOList = decodedList;
[decoder finishDecoding];
[decoder release];
[theData release];
}
Это делает семантику управления памятью понятной и освобождает память как можно быстрее.
(1) Вы можете взять на себя управление, используя свои собственные локальные пулы автоматического выпуска.Подробнее об этом см. Руководство по программированию управления памятью Apple.
Не беспокойтесь о сохранении счета, беспокойтесь о балансе в методе. То, что вы делаете в этом методе, выглядит правильно, если предположить, что venueIOList
является переменной экземпляра.
Чтобы немного расширить мой ответ: разархиватор может сохранять ваши данные во время операции разархивирования, а затем отправлять данные -autorelease
, когда это будет сделано, вместо -release код>. Поскольку вы этого не делали, это не то, что вам нужно заботиться.
Источником просвещения в области управления памятью, связанным с refcount, по-прежнему является, IMO, " Обними меня, используй меня, освободи меня " от Stepwise.
Ваш код правильный; нет утечки памяти.
theData = [NSData dataWithContentsOfFile:inFile];
эквивалентно
theData = [[[NSData alloc] initWithContentsOfFile:inFile] autorelease];
В этот момент theData имеет счетчик ссылок 1 (если меньше, он будет освобожден). Счетчик ссылок будет автоматически уменьшен в некоторый момент в будущем за счет пула автоматического выпуска.
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
Объект декодера сохраняет ссылку на данные, которая увеличивает его счетчик ссылок до 2.
После возврата метода пул авто-релиза уменьшает это значение до 1. Если вы освободите данные в конце этого метода, счетчик ссылок станет равным 0, объект будет освобожден, и ваше приложение потерпит крах, когда вы попытаетесь используйте это.