1

好的,我是一位经验丰富的 C++ 开发人员。我在尝试构建一个相当充实的 Cocoa 应用程序时正在学习 Objective-C。在为这个项目做准备的同时,我用 Cocoa 做了一些更简单的应用程序,我认为我对大多数概念都有很好的掌握。

内存管理范式对我来说仍然有点模糊,所以我使用内存分析器构建以帮助我立即发现问题,老实说,它非常棒,并且在帮助我理解 Objective-C 内存管理方面做得更多的文档。

所以这是我的问题。

performSelector:onThread:withObject:waitUntilDone:我有两个使用该方法相互通信的线程。它们在彼此之间传递作为withObject:方法参数传入的对象。

示例代码如下:

- (BOOL)postMessage:(id <MessageTarget>)sender messageId:(NSInteger)msgId messageData:(void*)data
{
    // message is allocated and retained here. retain count will be +2
    Message* message = [[[Message alloc] initWithSender:sender messageId:msgId messageData:data] retain];
    if(message)
    {
        // message will be released in other thread.
        [self performSelectorOnMainThread:@selector(messageHandler:) withObject:message waitUntilDone:NO];
        // message is released here and retain count will be +1 or 0 depending on thread ordering
        [message release];
        return YES;
    }
    return NO;
}

- (BOOL)sendMessage:(id <MessageTarget>)sender messageId:(NSInteger)msgId messageData:(void*)data messageResult:(void**)result
{
    // message is allocated and retained here. retain count will be +2
    Message* message = [[[Message alloc] initWithSender:sender messageId:msgId messageData:data] retain];
    if(message)
    {
        // message will be released in other thread. retain count will be +1 on return
        [self performSelectorOnMainThread:@selector(messageHandler:) withObject:message waitUntilDone:YES];
        if(result)
            *result = [message result];
        // message is released here and retain count will be 0 triggering deallocation
        [message release];
        return YES;
    }
    return NO;
}

- (void)messageHandler:(Message*)message
{
    // message will have a retain count of +1 or +2 in here based on thread execution order
    if(message)
    {
        switch ([message messageId])
        {
            case 
               ...
            break;

            default:
               ...
            break;
        }
        // message is released here bringing retain count to +1 or 0 depending on thread execution ordering
        [message release];
    }
}

分析器抱怨Message分配的对象可能泄漏postMessage:sendMessage:但对象在messageHandler:. 代码运行正确并且没有泄漏,我怀疑分析器根本无法看到Message对象正在不同的线程中释放。

现在,如果您想知道为什么我在 post/send 方法中而不是在messageHandler:方法中进行第二次保留,那是因为该postMessage:方法是异步的,并且[message release]在 post 方法中可能会在[message retain]in之前执行messageHandler:,让我无效的对象。如果我在该方法的情况下这样做会很好,sendMessage:因为它是同步的。

那么有没有更好的方法来满足内存分析器的要求呢?或者也许是一种向内存分析器提示该对象实际上正在被释放的方法?

更新:

Torrey 在下面提供了答案,但我想澄清我必须做的与他的建议不同的事情。

messageHandler:他建议在我的方法上使用一个属性,如下所示

- (void)messageHandler:(Message*) __attribute__((ns_consumed)) message;

这不太奏效,因为对象正在被传递performSelector:,而分析器没有看到它被传递给messageHandler:

由于performSelector:调用是由 Cocoa 定义的,而不是由我定义的,所以我不能向它添加属性。

解决此问题的方法是将调用包装performSelector:如下:

- (void)myPerformSelector:(SEL)sel onThread:(NSThread*)thread withObject:(id) __attribute__((ns_consumed)) message waitUntilDone:(BOOL)wait;
{
    [self performSelector:sel onThread:thread withObject:message waitUntilDone:wait];
}

然后您可以调用包装函数,分析器将看到该属性,而不会抱怨不平衡的保留/释放对。最后我不喜欢额外的间接来摆脱警告,所以我使用了预处理器,如下面的评论中所述。但我可以看到使用这种方法可能有用的情况。

4

2 回答 2

2
于 2012-07-09T05:03:52.677 回答
0

ns_consumed通过明智地使用 Clang 的属性,您应该能够使分析器满意。正如您所建议的,这给内存分析器一个提示,即在函数调用完成后将向参数发送释放消息。你会像这样使用它:

- (void)messageHandler:(Message*) __attribute__((ns_consumed)) message

Clang 分析器文档中有更多关于 Cocoa 内存管理注释的信息。您可能希望将属性设置包装在 NS_COSUMED 宏中,以便与该页面上建议的其他编译器兼容。

于 2012-07-08T00:25:11.097 回答