在类之间插入sortedArrayUsingSelector和/或initWithArray内存泄漏

时间:2011-10-02 01:09:55

标签: iphone sorting memory-management memory-leaks 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];
}

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;

}

仪器泄漏追踪:

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:)];

,当类试图从View Controller didSelectRowAtIndexPath方法中访问数组时,我得到一个EXC_BAD_ACCESS崩溃(可能是下次访问数组时,我相信)。这是方法。它在第二条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 :(得分:0)

在viewController代码中,分配一个InstalledDataTracker对象,然后将其传递给retain属性。这会导致保留计数为2而不是1.稍后,当您释放dataTracker对象时,只会将保留计数减少一个。

解决此问题的最简单方法是删除self.前缀,以便调用属性访问者执行的自动retain。实际上,我建议您不要在initdealloc方法中使用点语法。关于这个问题有一些争论,但总的来说,我认为除非你有充分的理由这样做,否则最好避免打电话给你的财产访问者。

以下是我的写作方式:

- (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 :(得分:0)

  

这是因为,据我所知,sortedArrayUsingSelector只返回指向旧数组的指针,而不是真正的副本,所以如果我想保留数组,我需要复制值。

这是一种误解。类似于其他类似函数的sortedArrayUsing...返回一个数组,该数组包含与原始数组中相同的指针VALUES。并且,当进行排序的数组复制时,指向的OBJECTS的引用计数递增(即,在每个复制的指针上完成retain)。因此,排序后的数组和原始数据都是“等于”,并且对象“拥有”的次数比其他对象都要多。 (事实上​​,检查两个阵列的内部结构,除了你注意到一个是排序的而另一个不是。)

所以绝对不需要制作排序数组的附加副本。

当您创建该附加副本时,您正在使用[[alloc] init...]操作返回保留的数组。然后将该数组返回给调用者而不对其进行autorelease,这意味着如果调用者没有明确地release它会泄漏它,并且意味着分析器会抱怨它(因为你只能返回copy...等人保留的对象。