我有一个C ++库,我希望将其作为Objective-C框架公开,因此对Objective-C开发人员来说更容易使用。在结束C ++库时,我遇到了一个处理自动释放对象和线程的特殊问题。
该库的一个特性是开发人员可以注册一个“记录器”,用于接收来自库的回调的通知消息。来自库的通知使用C ++类型并从另一个(POSIX)线程接收,因此我创建了一个私有C ++包装类来处理它:它接收回调,将char *参数转换为NSString,并传递给它到用户提供的Objective-C记录器实例。这一切都很好,看起来像这样:
// Is called from the C++ library from another posix thread
void ObjCLoggerWrapper::LogMessage(const char *message)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Pass string to the user-provided Objective-C instance called "Logger"
[Logger logMessage:[[[NSString alloc] initWithUTF8String:message] autorelease]];
[pool release];
}
作为用户回调的一个例子,我编写了这个简单的方法来收集用户类实例中的NSString成员m_text中的所有日志记录(在别处使用,但这是无关紧要的。)
-(void) logMessage: (NSString*)message
{
@synchronized(self)
{
m_text = [m_text stringByAppendingFormat:@"%04d: %@\r\n", m_lineno++, message];
}
}
到目前为止一切顺利。或者我想。但这是牛肉:
用户回调方法中的所有自动释放对象都属于包装器的NSAutoreleasePool,因此在回调完成时会被释放。
糟糕!这意味着我的m_text字符串(由stringByAppendingFormat消息隐式创建为自动释放的对象)将在logMessage完成后释放并变为僵尸。下次访问时,代码将崩溃。当然,用户肯定并且理所当然地不期望这样。在弄清楚发生了什么之前,我自己不得不挠头几次。
所以我的问题是:当从另一个线程回调用户代码时,我们应该如何处理自动释放的对象?
我看到了几种可能的选择。没有一个是完美的,谷歌没有帮助(因此这个问题)。
只是说清楚:重写我自己的包装是没问题的。我的主要优先事项是创建一个能够为Objective-C框架的用户平稳无缝地工作的解决方案。谢谢!
答案 0 :(得分:2)
这不是您的自动释放方法的问题。你的方法听起来很健康
问题是logMessage
因为书面存在根本缺陷。任何时候在调用之间都会消耗一个封闭的自动释放池,它就会失败。主运行循环(模优化)在每个事件循环旋转时消耗其自动释放池,因此在那种情况下这将失败就好了。
几个FWIW:
[Logger logMessage:[[[NSString alloc] initWithUTF8String:message] autorelease]];
可以写
[Logger logMessage:[NSString stringWithUTF8String:message]];
并且[pool drain]
优先于[pool release]
答案 1 :(得分:1)
如果不确切知道你的图书馆做了什么,很难推荐一种方法。但是,调用 performSelectorOnMainThread 是一种相当安全的策略。鉴于您不希望日志消息需要立即执行操作,等待主线程执行您的回调应该没问题。