1

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

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

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

// 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 ()  
4

2 回答 2

2

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

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

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

于 2012-12-19T00:27:29.470 回答
1

init...当您在 ARC 中使用方法时,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:,您(通常)会得到一个自动释放的对象。它将在运行循环结束时释放。如果您在创建自动发布的字典后立即记录它,它仍然存在并且有效。

于 2012-12-18T23:48:43.473 回答