【问题标题】:Objects not being released in Objective-C++ with ARC没有在带有 ARC 的 Objective-C++ 中发布的对象
【发布时间】:2012-12-18 23:35:52
【问题描述】:

我发现在 Objective-C++ 类中创建对象时的行为有所不同。

如果我使用dictionaryWith 和numberWith 创建一个包含NSNumber 对象的NSDictionary,那么这些对象永远不会被释放。如果我使用allocinitWith 创建它们,那么它们就会被清理干净。

我没有在同一个项目的 Objective-C 类中看到这一点。该项目已启用 ARC。我正在使用 Xcode 4.5.2 中的分配分析工具,查看 CFNumber 和 __NSDictionaryl 的“# Living”值。

// These objects will NOT be released.
NSDictionary* dict1 = [NSDictionary dictionaryWithObjectsAndKeys:
  [NSNumber numberWithUnsignedInt:val1], @"val1",
  [NSNumber numberWithUnsignedInt:val2], @"val2",
  [NSNumber numberWithUnsignedInt:val3], @"val3",
  nil];    
dispatch_async(dispatch_get_main_queue(), ^{
  [[NSNotificationCenter defaultCenter]
  postNotificationName:KEY_NET_STATS_VIEW_UDATE object:nil userInfo:dict1];
});

// These objects *will* be released.
NSDictionary* dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
  [[NSNumber alloc] initWithUnsignedInt:val1], @"val1",
  [[NSNumber alloc] initWithUnsignedInt:val2], @"val2",
  [[NSNumber alloc] initWithUnsignedInt:val3], @"val3",
  nil];    
dispatch_async(dispatch_get_main_queue(), ^{
  [[NSNotificationCenter defaultCenter]
  postNotificationName:KEY_NET_STATS_VIEW_UDATE object:nil userInfo:dict2];
});

使用 alloc/initWith 编写代码对我来说没有问题,但我想了解为什么会有差异。我读过的所有内容都表明它们在 ARC 下应该是等效的。

调用此代码时的堆栈跟踪。以下都是 C++,顺便说一句。

#0  0x001fbbe4 in ItRtpSessionManageriOS::OnItRtpOutgoingStatsUpdate(ItRtpSession&, ItRtpSessionManager::ItRtpStats const&)  
#1  0x0007018a in CSceApp::OnItRtpOutgoingStatsUpdate(ItRtpSession&, ItRtpSessionManager::ItRtpStats const&)  
#2  0x0006b808 in ItRtpSession::CallStatsUpdateCallback(ItRtpSessionManager::ItRtpStats const&)  
#3  0x0006ab1e in ItRtpSessionSharedCommXYZ::UpdateOutgoingStats(unsigned long, unsigned long)  
#4  0x0006a958 in ItRtpSessionSharedCommXYZ::Update(unsigned int, unsigned int)  
#5  0x000764ca in CSceApp::EvTimerServiceMgrAwaken(bool, unsigned int, void*)  
#6  0x00076908 in non-virtual thunk to CSceApp::EvTimerServiceMgrAwaken(bool, unsigned int, void*)  
#7  0x002a0134 in xyz::CServicingThread::Activate(unsigned long long, bool*)   
#8  0x0029fb98 in xyz::CServicingThread::Behavior()  
#9  0x0029fc34 in non-virtual thunk to xyz::CServicingThread::Behavior()  
#10 0x002578de in xyz::CAliveObj::StartMechanism(void*)  
#11 0x00259f9e in xyz::CThread::ThreadEntry(void*)  
#12 0x348b5310 in _pthread_start ()  
#13 0x348b51d8 in thread_start ()  

【问题讨论】:

    标签: objective-c automatic-ref-counting objective-c++


    【解决方案1】:

    在 ARC 下,有两种利用自动释放池的机制 - 作为运行循环的一部分,并使用 @autoreleasepool 绑定到范围。在第一种情况下,池仅在运行循环结束时耗尽,或者在自动释放池超出范围时,在第二种情况下。

    所以,如果你不让 runloop 运行,你放在那里的任何东西都不会被耗尽。同样,如果您在某处使用 @autoreleasepool 但没有退出该范围 - 也许您正坐在某个地方的紧密循环中,或者在范围内阻塞 - 那么您将看不到池耗尽。

    鉴于此,您应该查看您的代码,看看是否发生了这种情况。很遗憾,除非您发布更多代码,否则我们将无法帮助您查明问题。

    【讨论】:

    • 这段代码没有循环,不会阻塞。如果我在 @autoreleasepool 块中创建和使用对象,那么它们就会被释放(池耗尽)。或者,如果我使用 alloc/init 而不是类方法,那么池就会耗尽。接受这个答案是因为它让我尝试了更多的事情。我不能说我完全理解这种行为,但我必须继续前进。谢谢
    • 如果您在代码中设置断点并在此处发布堆栈跟踪,它可能会提供提示。也许你坐在上面的任何代码都在做一些奇怪的事情。
    • 对,所以,这里的关键因素是您使用的是非 Cocoa 线程。最有可能的问题是该调用堆栈中根本没有设置自动释放池,因此您自动释放的任何内容都会泄漏。您应该在应用程序的标准错误或控制台输出中看到有关此的错误消息。如果不是,则可能是某些东西 设置了一个自动释放池,但从未耗尽它。您将不得不查看代码的文档(或实现)。特别是,我会查看您的 xyz 库 - 如果在任何地方,那将是它的合乎逻辑的地方。
    【解决方案2】:

    当您在 ARC 中使用 init... 方法时,将在编译时对该对象的最后一次引用之后插入一个 release 调用。如果你在初始化之后没有在那个块中引用dict2,它将像下面的pre-ARC代码一样编译:

    NSNumber *n1 = [[NSNumber alloc] initWithUnsignedInt:val1];
    NSNumber *n2 = [[NSNumber alloc] initWithUnsignedInt:val2];
    NSNumber *n3 = [[NSNumber alloc] initWithUnsignedInt:val3];
    NSDictionary* dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
      n1, @"val1",
      n2, @"val2",
      n3, @"val3",
      nil];
    [n1 release];
    [n2 release];
    [n3 release];
    [dict2 release];
    

    (如果你想保留它,你需要对它进行强引用)

    相比之下,当你使用像dictionaryWithObjectsAndKeys: 这样的类方法时,你(通常)会得到一个自动释放的对象。它将在运行循环结束时释放。如果您在创建自动发布的字典后立即记录它,它仍然存在并且有效。

    【讨论】:

    • 感谢@DrummerB 问题是使用类方法创建的对象没有自动释放。我没有显示我所有的代码。创建字典后,我在 NSNotificationCanter 帖子中使用它。还有,为什么 Objective-C 和 Objective-C++ 有区别?
    • @DrummerB:ARC 实际上足够聪明,可以在很多情况下“捕获”类方法返回的自动释放对象,这样它们就不会进入自动释放池。
    • @KevinBallard 这就是我说“通常”的原因?还是应该是“有时”?
    猜你喜欢
    • 1970-01-01
    • 2012-05-23
    • 2014-06-19
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多