【问题标题】:memory access error when releasing a newly created object释放新创建的对象时出现内存访问错误
【发布时间】:2009-12-31 15:00:21
【问题描述】:

我无法理解为什么释放我刚刚创建的对象会导致我的应用程序出现内存访问错误。

我正在创建一堆对象并将它们添加到 NSMutableArray。将它们添加到数组后,我释放它们。该功能第一次运行时,一切顺利。该函数第二次运行时,释放对象会使应用程序崩溃,我不知道为什么。

这里是有问题的函数的一部分(完整的项目源代码在这里:http://github.com/cpjolicoeur/echowaves-notifier-osx

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [connection release];
    NSString *responseString = [[NSString alloc] initWithData:echowaves.responseData encoding:NSUTF8StringEncoding];
    [echowaves.responseData release];

    ...more code...

    [[echowaves updatedConvos] removeAllObjects];
    NSDictionary *dictionary = [responseString JSONValue];
    if ( [dictionary count] ) {
        for (NSDictionary *subscription in dictionary ) {
            UpdatedConvo *convo = [[UpdatedConvo alloc] initWithConvoName:[[subscription objectForKey:@"subscription"] objectForKey:@"convo_name"]
                                                                 convoURI:[[subscription objectForKey:@"subscription"] objectForKey:@"conversation_id"]
                                                              unreadCount:[[[subscription objectForKey:@"subscription"] objectForKey:@"new_messages_count"] integerValue]];


            [[echowaves updatedConvos] addObject:convo];
            [convo release];  // THIS LINE CAUSES CRASH 2ND TIME THROUGH
        }
    } else {

        ...more code....

    }
}            

应用程序通过该函数第二次在[convo release] 行崩溃。由于 convo 对象是在它之前的两行创建的,我不知道为什么。

我的印象是UpdatedConvo *convo = [[UpdatedConvo alloc] initWithConvoName...] 调用将创建对象并将其保留计数为 1。然后当我将对象添加到 [echowaves updatedConvos] NSMutableArray 对象时,它应该将保留计数增加 1 (现在计数为 2)。我已经完成了那个“临时”convo 对象,所以我然后释放它,这应该将它的保留计数移回 1,对吗??

我在这里错过了什么?

要让应用程序成功运行,我必须注释掉 [convo release] 行。不过我不知道为什么,我觉得这可能给我带来了缓慢的内存泄漏,因为新创建的 UpdatedConvo 对象没有正确地从内存中释放出来。

【问题讨论】:

    标签: objective-c memory-management memory-leaks


    【解决方案1】:

    您的方法应该有效。一种常见的模式是创建一个对象,将其添加到字典中,然后释放它。

    它不起作用的事实表明问题出在其他地方。首先要检查的是UpdatedConvo 类本身。它的dealloc 方法有什么花哨的功能吗?

    编辑:问题肯定出在UpdatedConvo。您在init 方法中分配您的实例变量(使用=):

    - (id)initWithConvoName:(NSString *)convoName
                   convoURI:(NSString *)convoURI
                unreadCount:(int)updatesCount 
    {
        if ( self = [super init] ) {
            ewURI = convoURI;
            ewName = convoName;
            newMessagesCount = updatesCount;
        }
        return self;
    }
    

    然后你在dealloc 中调用release

    - (void)dealloc {
        [ewURI release];
        [ewName release];
        [super dealloc];
    }
    

    在 init 方法中使用= 不会增加保留计数,因此当您释放它们时,实际上您将保留计数减少了太多。

    【讨论】:

    • 我不认为它有什么特别之处。它在自定义 init 方法期间设置的两个 NSString 实例变量上调用 release,然后只调用 [super dealloc]
    • 所以,因为我正在对实例变量进行“简单赋值”而不是保留它们,所以它们实际发生了什么。它们在对象生命周期中存在多长时间?另外,为什么应用程序在第一次通过循环时可以正确运行,但在后续运行期间却不能?如果代码不正确(我相信你确实如此),为什么它第一次“工作”正常?
    • second 时间,但是,您对removeAllObjects 的调用将导致调用 dealloc 方法,这将保留那些成员变量(实际上,最初用于设置它们的对象)处于未定义状态。之后的确切故障模式很难确定。最有可能的怀疑是,沿着这条线的某个地方,一条消息被发送到一个不再存在的对象(也称为僵尸)。这就是为什么此类对象被称为僵尸的原因:它们已被杀死(释放),但您仍有指向它们的指针,您可能稍后会发送消息。
    • 通过不保留或复制它们,您首先依赖于保存它们的任何容器。在这种情况下,[responseString JSONValue]。只要原始来源可用,这些对象就会存在。请注意,即使在对象被释放后,您也可以向对象发送消息,只要内存中没有任何内容覆盖它们。
    • retaincopy 属性仅在您使用属性访问器时生效,例如 [myObject setName:@"name"];myObject.name = @"name"; 您可以在 init 方法中使用此类访问器,但它是通常气馁。最好的办法是在init方法中手动调用retaincopy
    【解决方案2】:

    正如 e.James 已经提到的:UpdatedConvo 似乎是您的问题。
    我查看了您发布的 github 项目。 可能你在 UpdatedConvo 中过度释放:

    - (void)dealloc 
    {
        [ewURI release];
        [ewName release];
        [super dealloc];
    }
    

    您既没有在类中创建也没有保留 ewURI 和 ewName。
    所以你不应该在那里释放它。

    更新: 修正了一个误导性的错字(我在最后一句中写了“释放”而不是“保留”)

    更新 2: 我会通过 Instruments.app 使用“Zombie”和“Leaks”预设运行您的代码。
    如果您以前没有尝试过 Instruments,这是一个很好的机会来看看它的工作原理。

    【讨论】:

    • 我确实通过“Leaks”运行它并看到一堆 UpdatedConvo 对象泄漏。但我预料到了,因为我没有释放它们。我以前从未尝试过“僵尸”
    • 每当您的代码过度释放对象时,“僵尸”工具都会向您发出警告。 (在您的 UpdatedConvo 课程中就是这种情况)。 e.James 在他的 cmets 中提供了非常详细的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 2016-09-27
    • 2014-04-02
    • 1970-01-01
    • 2014-08-02
    相关资源
    最近更新 更多