【问题标题】:Plugging a sortedArrayUsingSelector and/or initWithArray Memory Leak Between Classes在类之间插入 sortedArrayUsingSelector 和/或 initWithArray 内存泄漏
【发布时间】:2011-11-29 06:05:52
【问题描述】:

我一直在努力解决这个内存泄漏问题,所以我希望社区可以提供一些帮助。内存管理仍然是我正在努力理解的一个问题(是的,我有内存管理指南)。

根据 Instruments Leak 工具,当我从相关屏幕向后导航(带导航控制器的后退按钮)时,我正在泄漏 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];
}

视图控制器中的泄漏方法

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

,当类尝试从视图控制器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];

}

【问题讨论】:

    标签: iphone sorting memory-management memory-leaks nsarray


    【解决方案1】:

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

    修复它的最简单方法是删除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];
    }
    

    【讨论】:

    • 根据 Leaks 的报告,看起来这不是您的问题。您是否有可能在其他地方使用了 self.orderedZonesArray = 语法?也许在您的模型类的-init 方法中?
    • 感谢您的提示。我更改了代码,不再泄漏数组。但是,现在我在导航到相关屏幕的 时遇到了崩溃。即我推动视图控制器,它正确加载。我点击“返回”,它释放了(仪器中没有泄漏)。但是,如果我再次尝试推动视图控制器,我会崩溃。有任何想法吗?再次感谢。
    • 嗯。现在听起来像一个不同的问题。控制台崩溃时有什么有用的东西吗?您是否尝试过运行调试器来查看崩溃时的堆栈跟踪?
    • 是的,现在它正在第二次推送,但没有填充 tableView。在第二次推送时,控制台会产生:*** __NSAutoreleaseFreedObject(): release of previously deallocated object (0x4f1d720) ignored,然后如果我第二次回击,我会在[_dataTracker release];[_dataTracker release];
    • 不。我只是做了一个全球搜索来确定。
    【解决方案2】:

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

    这是一种误解。 sortedArrayUsing...,与其他此类函数类似,返回一个数组,其中包含与原始数组中相同的指针 VALUES。并且,在进行排序后的数组复制时,所指向的对象的引用计数会增加(即,retain 在每个复制的指针上完成)。所以排序后的数组和原始数组都是“相等的”,并且都没有比另一个更“拥有”对象。 (事实上​​,检查这两个数组的内部结构,您将无法分辨哪个是从哪个复制而来的,除非您注意到一个已排序而另一个未排序。)

    所以绝对没有必要对排序后的数组进行额外的复制。

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

    【讨论】:

    • 分析器不应该抱怨。他将分配的变量分配给 ivar,而不仅仅是返回一个临时变量。该代码块(恕我直言)上的内存管理看起来不错,即使该技术可能有点与众不同;)
    • 是的,我错过了它是一个属性。不过还是有一些非常奇怪的代码。
    • 我希望你能详细说明什么是奇怪的?我在 Obj-C 上自学成才,并且仍然非常了解最佳技术。如果我因为我的方法存在根本性缺陷而遇到问题,我很想知道这种事情的更好/更简单/更常见的实现可能是什么!
    • 请参阅上面对我原来的问题的编辑。当我不复制排序后的数组时,会出现 EXC_BAD_ACCESS 崩溃,这就是为什么我首先添加了 alloc/init 副本:/
    • 您的主要问题显然是您还没有熟悉 Objective-C 存储管理方案——初始化和复制、保留和释放以及自动保留和解除分配。这需要一段时间,但你必须找到一个很好的参考资料(我有几本书在工作),否则你只是四处游荡。
    猜你喜欢
    • 2011-10-13
    • 1970-01-01
    • 2021-02-21
    • 1970-01-01
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    相关资源
    最近更新 更多