好的,我是一位经验丰富的 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];
}
然后您可以调用包装函数,分析器将看到该属性,而不会抱怨不平衡的保留/释放对。最后我不喜欢额外的间接来摆脱警告,所以我使用了预处理器,如下面的评论中所述。但我可以看到使用这种方法可能有用的情况。