Подключение sortedArrayUsingSelector и / или initWithArray для утечки памяти между классами - PullRequest
1 голос
/ 02 октября 2011

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

Согласно инструменту «Утечка инструментов», я пропускаю NSArray, как только перехожу назад (кнопка назад с Nav Controller) с соответствующего экрана. Ниже я показываю весь соответствующий код, который могу придумать, и могу поделиться большим, если это необходимо.

Я знаю, что я выделяю / инициирую массив в упорядоченной функции массива. Это потому, что, насколько я понимаю, 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 в View Controller

#import <UIKit/UIKit.h>

@class InstalledDataTracker;

@interface SBVC_LSC01_ZoneSelect : UIViewController <UITableViewDataSource, UITableViewDelegate> {

    ... other stuff...

    InstalledDataTracker *_dataTracker;
}

@property (nonatomic, retain) InstalledDataTracker *dataTracker;

.m init в View Controller

@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];
}

Метод утечки в контроллере вида

- (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;

}

Отслеживание утечек инструментов:

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, приводят к некоторому сбою / неправильному доступу в контроллере представления. Это способствует моей путанице из-за того, где правильно освобождать массив ...

Подробные ответы очень приветствуются. Мне еще предстоит многому научиться!

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

Edit:

@ Дэниел Хикс, когда я удаляю копию initWithArray отсортированного массива следующим образом:

orderedZonesArray = [unorderedZones sortedArrayUsingSelector:@selector(localizedCompare:)];

, я получаю сбой EXC_BAD_ACCESS, когда класс пытается получить доступ к массиву из метода View Controller 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];

}

Ответы [ 2 ]

0 голосов
/ 02 октября 2011

Это потому, что, насколько я понимаю, sortedArrayUsingSelector возвращает только указатели на старый массив, а не истинную копию, поэтому, если я хочу сохранить массив, мне нужно скопировать значения.

Это неверное толкование. sortedArrayUsing..., как и другие подобные функции, возвращает массив, который содержит те же значения VALUES указателя, что и в исходном массиве. И, поскольку была сделана копия отсортированного массива, счетчики ссылок на ОБЪЕКТЫ, на которые указывал, были увеличены (то есть retain было сделано для каждого скопированного указателя). Таким образом, отсортированный массив и оригинал являются «равными», и ни один из них не «владеет» объектами больше, чем другой. (На самом деле, изучая внутренности двух массивов, вы не сможете определить, из какого из них скопирован, за исключением случаев, когда вы заметили, что один отсортирован, а другой нет.)

Так что абсолютно не нужно делать дополнительную копию отсортированного массива.

Когда вы делаете эту дополнительную копию, вы используете операцию [[alloc] init...], которая возвращает сохраненный массив. Затем вы возвращаете этот массив вызывающей стороне, не выполняя autorelease, а это означает, что он утечет, если ваша вызывающая сторона явно не release, и означает, что Analyzer будет жаловаться на него (поскольку вы можете только вернуть сохраненный объект от copy... и др.)

0 голосов
/ 02 октября 2011

В вашем коде viewController вы выделяете объект InstalledDataTracker, а затем передаете его в свойство сохранения.В результате счет сохранения равен 2, а не 1. Позже, когда вы освобождаете объект dataTracker, вы уменьшаете счет хранения только на один.

Самый простой способ исправить это - удалить self.префикс, чтобы вы не вызывали автоматический retain, который выполняется средством доступа к свойству.На самом деле, я бы рекомендовал вообще не использовать точечный синтаксис в ваших init и dealloc методах.Есть некоторые споры по этому вопросу, но в целом я думаю, что лучше избегать звонить своим правообладателям, если у вас нет очень веских причин для этого.

Вот как я бы написал:

- (id)initWithPerson:(NSString *)person {
    if (self = [super init]) {
        ...other stuff...

        dataTracker = [[InstalledDataTracker alloc] init];
    }
    return self;
}

- (void)dealloc {
    ...other stuff...
    [dataTracker release];
    [super dealloc];
}
...