【问题标题】:Crash when releasing object created using initFromFile method释放使用 initFromFile 方法创建的对象时崩溃
【发布时间】:2010-10-05 12:12:26
【问题描述】:

我创建了一个名为“DateTracker”的对象,它符合 NSCoding,因此它包含 encodeWithCoder 和 initWithCoder 方法。当我初始化它时,我调用以下内容:

DateTracker *currentTracker = [[DateTracker alloc] initFromFile];

initFromFile 方法如下所示:

- (id)initFromFile { 
    NSString *filePath = [self dataFilePath];
    if ([[NSFileManager defaultManager] attributesOfFileSystemForPath:filePath error:NULL]) {
        NSData *data = [[NSMutableData alloc] initWithContentsOfFile:filePath];
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
        self = [unarchiver decodeObjectForKey:kDateDataKey];
        [unarchiver finishDecoding];
        [unarchiver release];
        [data release];
    }
    return self;
}

但是当我尝试打电话时

[currentTracker release];

我的应用程序崩溃了。

当我使用性能工具运行应用程序以检查内存泄漏时,它会抱怨我没有释放此对象。

任何想法我做错了什么?

【问题讨论】:

    标签: ios objective-c memory-management nscoding


    【解决方案1】:

    这一行:

    self = [unarchiver decodeObjectForKey:kDateDataKey];
    

    会给你带来麻烦。

    您正在做的是分配一个 DateTracker 对象 ([DateTracker alloc]),然后创建一个新的 DateTracker 对象(由-decodeObjectForKey:)并让“self”指针指向新对象。这样做有两个问题:

    • 你不再有对旧对象的引用,所以它被泄露了
    • 新对象未保留,因此它会消失(如果您尝试释放它会导致崩溃)

    我会说让对象替换自身的方法有点可疑。也许您最好将filePath 变量移到DateTracker 对象之外,然后通过以下方式取消归档:

    DateTracker *currentTracker = [[DateTracker unarchiveFromFile:filePath] retain];
    

    其中unarchiveFromFile: 是一个类方法,它基本上完成了initFromFile 所做的工作,而不会与self 混淆:

    + (DateTracker*)unarchiveFromFile:(NSString *)filePath { 
        DateTracker *result = nil;
        if ([[NSFileManager defaultManager] attributesOfFileSystemForPath:filePath error:NULL]) {
            NSData *data = [[NSMutableData alloc] initWithContentsOfFile:filePath];
            NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
            result = [unarchiver decodeObjectForKey:kDateDataKey];
            [unarchiver finishDecoding];
            [unarchiver release];
            [data release];
        }
        return result;
    }
    

    【讨论】:

    • 啊啊啊我明白了。如果我将文件路径初始化程序保留在 DateTracker 类中,它仍然可以工作吗?所以 unarchiveFromFile 方法的第一行会得到 filePath 字符串。
    • 另外,如果我只是将我的 initFromFile 方法更改为一个类方法并像这样调用它会发生什么:DateTracker *currentTracker = [DateTracker initFromFile]。不使用时会自动释放吗?或者我应该明确地保留和释放它?或者这只是一个坏主意......
    • 当然,filePath 可以是 DateTracker 类的静态成员。关于自动释放的要点是decodeObjectForKey: 将返回一个自动释放的对象(根据正常的 Cocoa 内存管理约定,因为它的名称中没有“Alloc”或“Copy”,所以你知道它返回一个自动释放的对象)目的)。所以,如果你想坚持下去,你需要保留/释放它。
    • 啊,太好了,感谢您的解释!那么当我创建如下 UIImage 时发生了什么: UIImage *image = [UIImage imageNamed:@"theImage.png"];由于它是一个类方法,它是自动释放的,对吗?但是如果我想再次使用它,我应该添加一条保留消息。但是如果我不加retain message,是不是就不用调用【image release】了?感谢您的帮助!
    • 另外,我对类方法有点不清楚。我不能使用类变量或引用其中的其他类方法?因此,要初始化正确的 filePath,我需要将其作为参数传递,或者按照您的建议将其设置为静态变量。但是如果我将它设置为静态变量,我不能确定文件系统不会改变并且它会不正确。如果我将它作为参数传递,我可能会在另一个类中错误地初始化它。我的 dataFilePath 方法当前位于 DateTracker 类中,所以我可以确定它总是返回相同的结果...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多