#iphone #sorting #memory-management #memory-leaks #nsarray
#iPhone #сортировка #управление памятью #утечки памяти #nsarray
Вопрос:
Я уже некоторое время пытаюсь устранить эту утечку памяти, поэтому надеюсь, что сообщество сможет оказать некоторую помощь. Управление памятью по-прежнему остается проблемой, которую я пытаюсь понять (и да, у меня есть руководство по управлению памятью).
Согласно инструменту утечки инструментов, я пропускаю NSArray, как только я перемещаюсь назад (кнопка возврата с контроллером навигации) с соответствующего экрана. Ниже я показываю весь соответствующий код, который я могу придумать, и могу поделиться больше, если это необходимо.
Я знаю, что я выделяю / инициализирую массив в функции упорядоченного массива. Это потому, что, насколько я понимаю, sortedArrayUsingSelector
возвращает только указатели на старый массив, а не истинную копию, поэтому, если я хочу сохранить массив, мне нужно скопировать значения.
Тогда проблема в том, как мне передать этот отсортированный массив в другой класс, сохраняя при этом правильное управление своим правом собственности на него? Я освобождаю его в dealloc , и я освобождаю его, если функция собирается присвоить новое значение и т.д. Но я не знаю, правильно ли я это делаю.
Как я уже сказал, я действительно изо всех сил пытаюсь правильно понять, как правильно манипулировать всеми элементами управления памятью, поэтому любая помощь будет высоко оценена.
.h файл соответствующего класса модели
@interface InstalledDataTracker : NSObject {
...other code...
NSArray *orderedZonesArray;
...other code...
}
@property (nonatomic, retain) NSArray *orderedZonesArray;
.m файл соответствующего класса модели
@synthesize orderedZonesArray;
...other code...
- (NSArray *)orderedZonesArray {
if (!orderedZonesArray || installedDataChangedSinceLastRead) {
if (orderedZonesArray) {
[orderedZonesArray release];
}
NSArray *unorderedZones = [NSArray arrayWithArray:[self.installedAreas allKeys]];
orderedZonesArray = [[NSArray alloc] initWithArray:[unorderedZones sortedArrayUsingSelector:@selector(localizedCompare:)]];
}
return orderedZonesArray;
}
- (void) dealloc {
...other code...
[orderedZonesArray release], orderedZonesArray = nil;
[super dealloc];
}
.h в контроллере представления
#import <UIKit/UIKit.h>
@class InstalledDataTracker;
@interface SBVC_LSC01_ZoneSelect : UIViewController <UITableViewDataSource, UITableViewDelegate> {
... other stuff...
InstalledDataTracker *_dataTracker;
}
@property (nonatomic, retain) InstalledDataTracker *dataTracker;
.m инициализация в контроллере представления
@synthesize dataTracker = _dataTracker;
- (id)initWithPerson:(NSString *)person {
if (self = [super init]) {
...other stuff...
self.dataTracker = [[InstalledDataTracker alloc] init];
}
return self;
}
- (void)dealloc
{
...other stuff...
[self.dataTracker release];
[super dealloc];
}
Leaking Method in View Controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
AbbreviationLookup *lookup = [[AbbreviationLookup alloc] init];
NSString *abbreviatedZone = [self.dataTracker.orderedZonesArray objectAtIndex:[indexPath section]];
cell.textLabel.text = [lookup zoneForAbbreviation:abbreviatedZone];
[lookup release];
return cell;
}
Instruments Leak Trace:
0 libSystem.B.dylib calloc
1 libobjc.A.dylib class_createInstance
2 CoreFoundation __CFAllocateObject2
3 CoreFoundation [__NSArrayI __new::]
4 CoreFoundation -[NSArray initWithArray:range:copyItems:]
5 CoreFoundation -[NSArray initWithArray:]
6 -[InstalledDataTracker orderedZonesArray]
7 -[SBVC_LSC01_ZoneSelect tableView:cellForRowAtIndexPath:]
Вещи, которые я пробовал
orderedZonesArray = [[[NSArray alloc] initWithArray:[unorderedZones sortedArrayUsingSelector:@selector(localizedCompare:)]] autorelease];
return [orderedZonesArray autorelease];
И куча других вещей, которые я не могу вспомнить. Многие попытки, которые я предпринял, чтобы правильно «освободить» право собственности, созданное alloc / init, приводят к некоторому сбою / плохому доступу в контроллере представления. Это вносит свой вклад в мое замешательство по поводу того, где правильно освободить массив…
Подробные ответы очень приветствуются. Мне еще многому предстоит научиться!
Большое спасибо. (Кроме того, мне пришлось изменить некоторые имена классов и методов для обеспечения безопасности проекта, поэтому, если что-то не совпадает, пожалуйста, сообщите об этом, и я перепроверю опечатку)
Редактировать:
@Daniel Hicks, когда я удаляю initWithArray
копию отсортированного массива, выполните следующие действия:
orderedZonesArray = [unorderedZones sortedArrayUsingSelector:@selector(localizedCompare:)];
я получаю сбой EXC_BAD_ACCESS, когда класс пытается получить доступ к массиву из метода контроллера представления didSelectRowAtIndexPath
(я полагаю, вероятно, при следующем обращении к массиву). Вот метод. Он выходит из строя во второй строке NSLog, поэтому я оставил это для хорошей оценки:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"indexPath = %@", indexPath);
NSLog(@"self.dataTracker.orderedZonesArray = %@", self.dataTracker.orderedZonesArray);
NSString *abbreviatedZone = [self.dataTracker.orderedZonesArray objectAtIndex:[indexPath section]];
SBVC_LSC02_ZoneSelect *slz2 = [[SBVC_LSC02_ZoneSelect alloc] initWithPerson:self.selectedPerson andZone:abbreviatedZone];
[self.navigationController pushViewController:slz2 animated:YES];
[slz2 release];
}
Ответ №1:
В вашем коде ViewController вы выделяете InstalledDataTracker
объект, а затем передаете его в свойство сохранения. В результате количество сохранений будет равно 2, а не 1. Позже, когда вы освобождаете объект dataTracker, вы уменьшаете количество сохранений только на единицу.
Самый простой способ исправить это — удалить self.
префикс, чтобы вы не вызывали автоматическое retain
выполнение, выполняемое средством доступа к свойствам. На самом деле, я бы рекомендовал вообще не использовать синтаксис dot в ваших init
dealloc
методах and . По этому вопросу ведутся некоторые споры, но в целом я думаю, что лучше избегать вызова ваших средств доступа к свойствам, если у вас нет для этого веских оснований.
Вот как я бы это написал:
- (id)initWithPerson:(NSString *)person {
if (self = [super init]) {
...other stuff...
dataTracker = [[InstalledDataTracker alloc] init];
}
return self;
}
- (void)dealloc {
...other stuff...
[dataTracker release];
[super dealloc];
}
Комментарии:
1. Судя по отчету Leakes, не похоже, что это ваша проблема. Возможно ли, что вы использовали
self.orderedZonesArray =
синтаксис где-то еще? Возможно, в-init
методе для вашего класса модели?2. Спасибо за совет. Я изменил свой код и больше не пропускаю массив. Однако теперь я получаю сбой во второй раз, когда я перехожу к соответствующему экрану. т.Е. Я нажимаю view controller, он загружается правильно. Я нажимаю «назад», он освобождает (без утечки в инструментах). Но затем, если я попытаюсь снова нажать на контроллер представления, я вылетаю. Есть идеи? Еще раз спасибо.
3. Хм. Теперь это звучит как другая проблема. Есть ли что-нибудь полезное в консоли при сбое? Вы пробовали запускать отладчик, чтобы просмотреть трассировку стека при сбое?
4. Да, теперь он нажимает второй раз, но не заполняет TableView. При втором нажатии консоль выдает:
*** __NSAutoreleaseFreedObject(): release of previously deallocated object (0x4f1d720) ignored
и затем, если я повторяю второй раз, я получаю сбой EXC_BAD_ACCESS в строке[_dataTracker release];
5. Нет. Я только что выполнил глобальный поиск, чтобы быть уверенным.
Ответ №2:
Это потому, что, насколько я понимаю, sortedArrayUsingSelector возвращает только указатели на старый массив, а не истинную копию, поэтому, если я хочу сохранить массив, мне нужно скопировать значения.
Это неправильное толкование. sortedArrayUsing...
, аналогично другим подобным функциям, возвращает массив, который содержит те же значения указателей, что и в исходном массиве. И, поскольку была сделана копия отсортированного массива, количество ссылок на ОБЪЕКТЫ, на которые указывались, увеличивалось (т. Е. retain
выполнялось для каждого скопированного указателя). Таким образом, отсортированный массив и оригинал являются «равными», и ни один из них не «владеет» объектами больше, чем другой. (Фактически, изучая внутренности двух массивов, вы не сможете определить, какой из них был скопирован, кроме как если бы вы заметили, что один отсортирован, а другой нет.)
Таким образом, нет абсолютно никакой необходимости создавать дополнительную копию отсортированного массива.
Когда вы делаете эту дополнительную копию, вы используете [[alloc] init...]
операцию, которая возвращает сохраненный массив. Затем вы возвращаете этот массив своему вызывающему, не выполняя autorelease
над ним никаких действий, что означает, что он будет протекать, если ваш вызывающий не делает этого явно release
, и это означает, что анализатор будет жаловаться на это (поскольку вы можете вернуть только сохраненный объект из copy...
et al).
Комментарии:
1. Анализатор не должен жаловаться. Он присваивает выделенную переменную ivar, а не просто возвращает временную переменную. Управление памятью в этом блоке кода выглядит нормально (ИМХО), даже если техника, возможно, немного необычна
;)
2. Да, я пропустил, что это свойство. Тем не менее, все еще довольно странный код.
3. Я был бы рад, если бы вы могли уточнить, что странно? Я самоучка по Obj-C и все еще очень быстро осваиваю лучшие методы. Если я столкнусь с проблемами, потому что мои методы здесь более фундаментально ошибочны, я бы хотел услышать, какой может быть лучшая / более простая / более распространенная реализация такого рода вещей!
4. Пожалуйста, посмотрите Редактирование моего первоначального вопроса выше. Я получаю сбой EXC_BAD_ACCESS, когда я не копирую отсортированный массив, поэтому я в первую очередь добавил копию alloc / init
:/
5. Ваша основная проблема, по-видимому, заключается в том, что вы еще не использовали схему управления хранилищем Objective-C — инициализацию, копирование, сохранение и освобождение, автоматическое сохранение и освобождение. Это занимает некоторое время, но вы должны найти хорошую ссылку (у меня есть пара книг на работе), или вы просто валяете дурака.