【问题标题】:Why does releasing my array crash my app?为什么释放我的阵列会使我的应用程序崩溃?
【发布时间】:2011-11-14 08:12:04
【问题描述】:

如果我在将第一个数组复制到第二个数组后释放它,我的应用程序就会崩溃。如果我自动释放第一个数组,一切正常。为什么?有没有更好的方法将第一个数组复制到第二个数组?

如果我调用这个方法,我会得到一个 ECX_BAD_ACCESS,我传递的是一个空数组

-(NSArray *)loadSystemDetails
{
    AssortedCodeSnippets *acs = [[AssortedCodeSnippets alloc] init];
    NSArray *details;
    NSString *fp = [self tempPathAndFileName:[self systemDetailsFileName]];
    if ([acs fileExistsAtPath:fp]) {
        NSArray *array = [[NSArray alloc] initWithContentsOfFile:fp];
        details = array;
        [array release];
    } else {
        NSLog(@"No File to Load");
        CreateSystem *cls = [[CreateSystem alloc] init];
        details = [cls loadData];
        [cls release];
        [self saveDataFile:details toPath:fp];
    }
    NSLog(@"details: %@",details);
    [acs release];
    return details;
}

如果我自动释放数组,它工作正常。

-(NSArray *)loadSystemDetails
{
    AssortedCodeSnippets *acs = [[AssortedCodeSnippets alloc] init];
    NSArray *details;
    NSString *fp = [self tempPathAndFileName:[self systemDetailsFileName]];
    if ([acs fileExistsAtPath:fp]) {
        NSArray *array = [[[NSArray alloc] initWithContentsOfFile:fp]autorelease];
        details = array;
    } else {
        NSLog(@"No File to Load");
        CreateSystem *cls = [[CreateSystem alloc] init];
        details = [cls loadData];
        [cls release];
        [self saveDataFile:details toPath:fp];
    }

【问题讨论】:

  • 坦率地说,在您了解指针是什么之前,您应该坚持使用普通的旧 C 语言,甚至 Java。 Objective-C 对于初学者来说太复杂了。
  • 嘿,放轻松,每个人都必须从某个地方开始,我可以将我的 C 知识写在邮票的背面,但我的开发似乎还不错:S

标签: objective-c ios nsarray


【解决方案1】:

让我们一步一步来

键:M = 释放/保留消息,C = 释放/保留消息之和

                                                              // +----+---+
                                                              // | M  | C |
                                                              // +----+---+
NSArray *array = [[NSArray alloc] initWithContentsOfFile:fp]; // | +1 | 1 |
details = array;                                              // |  0 | 1 |
[array release];                                              // | -1 | 0 |
                                                              // +----+---+

此时您可以看到您将返回 details,其计数为 0,因此已被释放 = 崩溃。

复制是错误的术语,因为您实际上不需要副本,因此您只希望指针 details 指向有效对象,因此以下内容会更正确

- (NSArray *)systemDetails
{
    NSString *filePath = [self tempPathAndFileName:[self systemDetailsFileName]];

    NSArray *details = [[[NSArray alloc] initWithContentsOfFile:filePath] autorelease];

    if (!details) {

        NSLog(@"No File to Load");
        CreateSystem *cls = [[CreateSystem alloc] init];
        details = [cls loadData];
        [cls release]; cls = nil;
        [self saveDataFile:details toPath:filePath];

    }

    NSLog(@"details: %@",details);

    return details;
}

这里我利用NSArray的方法

initWithContentsOfFile:

...[returns] 如果文件无法打开或文件内容无法解析为数组,则返回 nil

这减少了一些麻烦并使该方法更易于阅读。我还将变量名称扩展为有意义的名称(个人喜好)。

我还重命名了该方法,因为 load 艺术是多余的,因为本质上您正在返回系统详细信息,事实上它们正在被加载并不是方法调用者真正关心的问题。

还需要注意的是,其他答案建议您进行额外的保留/复制,然后记得稍后释放返回的结果。这违反了 cocoa 约定,因为方法名称不包含 new/init/copy,因此该方法的调用者不应最终拥有结果。

【讨论】:

  • 感谢您的回复和帮助。我仍在尝试找出整个 Objective-C 语言。
【解决方案2】:

当分配details 指向arraycls-loadData 方法的结果时,发送details 赋值retain 消息。一定要在-loadSystemDetails 方法之后的某个地方release details

【讨论】:

    【解决方案3】:

    在您的代码示例中,您实际上并未将array 复制到details。请记住,这两个变量都是数组的指针,而不是数组本身。因此,details = array 行只是将array 的位置复制到details。换句话说,在这一行之后,两个变量都指向内存中完全相同的数组。因此,当您调用release 时,内存中的对象将被释放,并且两者 detailsarray 现在都指向一个不存在的对象。如果要实际复制内存中的数组,请使用

    details = [array copy]
    

    请记住,当你想摆脱这个对象时,最终你必须在 details 上调用 release

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-16
      • 2012-01-24
      • 1970-01-01
      • 2014-06-27
      • 1970-01-01
      • 1970-01-01
      • 2015-08-13
      • 1970-01-01
      相关资源
      最近更新 更多