objective-c - 分析器提示多线程 Cocoa 应用程序中可能存在资源泄漏

标签 objective-c macos cocoa

好的,我是一名经验丰富的 C++ 开发人员。我正在快速学习 Objective-C,同时尝试构建一个相当强大的 Cocoa 应用程序。在准备这个项目的同时,我用 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] 之前执行在messageHandler:会,给我留下一个无效的对象。如果我在 sendMessage: 的情况下这样做,效果会很好。方法,因为它是同步的。

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

更新:

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

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

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

这不太有效,因为对象被传递到 performSelector:并且分析器没有看到它被传递到 messageHandler:

performSelector: call 是由 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];
}

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

最佳答案

使用此 API 时,您不需要需要显式地将引用计数操作传输到辅助线程。

  • a) 当 waitUntilFinished 为 true 时,你的调用者持有一个引用,
  • b) 和 + [NSThread PerformSelector:… 背后的实现也是如此(请参阅:- [NSRunLoop PerformSelector:target:argument:order:modes:])。

在这种情况下,无需跨线程传递参数(或 self)的引用计数职责。

它将立即执行,或者 self 和参数(它是 objc 类型的)将在另一个线程的运行循环队列中保留(并且 self 和参数在对象执行完毕后被释放)执行选择器)。

(不要使用 ref-op __attribute__ 来关闭它)

关于objective-c - 分析器提示多线程 Cocoa 应用程序中可能存在资源泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11378992/

相关文章:

cocoa - 核心动画回调

iphone - NSMutableDictionary 从不同的类访问

java - 从 Java 执行外部程序

swift - 使用 NSSavePanel 将文件从 Bundle 保存/复制到桌面

javascript - Angular Javascript 导出 CSV。有没有办法在 Mac 和 Windows 中一致地指定文件扩展名

objective-c - 对于免费桥接数据类型使用哪个版本?

ios - UITextFieldDelegate textFieldShouldReturn 调用者

objective-c - Block没有在typeof中捕获self,为什么?

objective-c - NSPrintInfo 共享 PrintInfo 到底是如何共享的?

objective-c - 获取最前面应用程序当前选中的文本