objective-c - 来自另一个线程的回调中 NSAutoreleasePool 的最佳实践

标签 objective-c multithreading cocoa autorelease nsautoreleasepool

我有一个 C++ 库,我想将其公开为 Objective-C 框架,以便 Objective-C 开发人员更容易使用。在总结 C++ 库时,我遇到了一个处理自动释放对象和线程的特殊问题。

该库的一个特性是开发人员可以注册一个“记录器”,用于从库中接收通知消息作为回调。来自库的通知使用 C++ 类型并从另一个 (POSIX) 线程接收,所以我创建了一个私有(private) 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 完成时释放并成为僵尸。下次访问时,代码会崩溃。当然,用户当然不会期望这一点。在意识到发生了什么之前,我自己不得不挠头几次。

所以我的问题是:当从另一个线程回调用户代码时,我们应该如何处理自动释放的对象?

我看到了几种可能的选择。没有一个是完美的,谷歌也没有帮助(因此这个问题)。

  1. 告诉用户“不要在回调代码中创建自动释放对象”。不好:此类对象通常是非自愿创建的,例如通过 stringByAppendingFormat 和大量其他框架方法。除了稍后出现的难以调试的崩溃之外,没有任何警告。
  2. 没有 NSAutoreleasePool。如果用户尝试创建自动释放的对象,则缺少一个将导致警告。绝对不漂亮,但会以稳健的方式提醒用户注意问题。并且用户可以“仅仅”添加他自己的 NSAutoreleasePool 来解决这个问题。但同样:不漂亮。
  3. 没有 NSAutoreleasePool 并使用 performSelectorOnMainThread 在主线程上运行回调。任何新的自动释放对象都会在主线程的池中结束。我认为这是安全的,但欢迎发表评论——例如,回调能否始终在主线程上执行?这种方法需要在包装器中进行更精细的编码以避免线程死锁并等待结果,但这是我目前的首选。

只是说清楚:重写我自己的包装器是没有问题的。我的首要任务是创建一个可以为 Objective-C 框架的用户顺畅无缝地工作的解决方案。谢谢!

最佳答案

这不是您的自动释放方法的问题。你的方法看起来不错。 问题是编写的 logMessage 存在根本性缺陷。每当调用之间封闭的自动释放池耗尽时,它都会失败。主运行循环(模优化)在每次事件循环旋转时都会耗尽其自动释放池,因此在那种情况下,它会在那里失败同样严重。

几个 FWIW:

[Logger logMessage:[[[NSString alloc] initWithUTF8String:message] autorelease]];

可以写

[Logger logMessage:[NSString stringWithUTF8String:message]];

[pool drain]优于[pool release]

关于objective-c - 来自另一个线程的回调中 NSAutoreleasePool 的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8424199/

相关文章:

ios - 如何获取 UIImage 中形状的 UIBezierPath 或以特定形状裁剪 UIImage

objective-c - 表格重新加载后设置 UITableView FooterView

python - 在上下文管理器中捕获 threading.Timer 抛出的异常

multithreading - Lparallel.queue 线程安全吗?

objective-c - NSTask 没有从用户环境中获取 $PATH

objective-c - 旋转 UIImage 固定角度

ios - iOS 13 和 14 上的 NSKeyValuePopPendingNotificationLocal 崩溃

java - 让它漂亮 : processing an array concurrently

objective-c - 为什么自定义 setter 的存在会破坏我的自定义 getter?

objective-c - 如何获取登录用户的头像?