2012-12-18 62 views
1

在Objective-C++類中創建對象時,我看到了行爲上的差異。在Objective-C++中沒有發佈的對象使用ARC

如果我使用dictionaryWith和numberWith創建一個包含NSNumber對象的NSDictionary,那麼對象永遠不會被釋放。如果我使用allocinitWith創建它們,那麼它們會被清理得很好。

我在同一個項目的Objective-C類中沒看到這個。該項目啓用了ARC。我使用Xcode 4.5.2中的Allocations概要分析工具,查看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++,BTW。

#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() 

回答

2

在ARC下,有兩種利用自動釋放池的機制 - 作爲runloop的一部分,並使用@autoreleasepool綁定到作用域。池只在runloop結束時,在第一種情況下,或者當autorelease池超出範圍時,在第二種情況下耗盡。

所以,如果你不讓runloop運行,那麼你放在那裏的任何東西都不會被耗盡。同樣,如果你在某個地方使用了@autoreleasepool,但沒有退出該範圍 - 也許你坐在某個地方的一個緊密環路中,或者在範圍內阻塞 - 那麼你不會看到游泳池耗盡。

鑑於這一點,你應該看看你的代碼,看看是否發生了什麼。不幸的是,除非您發佈更多的代碼,否則我們無法幫助您找出問題所在。

+0

此代碼不在循環中,也不會阻止。如果我在@autoreleasepool塊中創建並使用這些對象,那麼它們將被釋放(池消耗)。或者,如果我使用alloc/init而不是類方法,則池消耗。接受這個答案,因爲它讓我嘗試更多的事情。我不能說我完全理解這種行爲,但我必須繼續前進。謝謝 – Android63

+0

如果您在代碼中設置斷點並在此處發佈堆棧跟蹤,則可能會提供提示。也許你坐在上面的任何代碼都在做一些奇怪的事情。 –

+0

增加了stracktrace。 – Android63

1

當您使用init...方法與ARC,一個release呼叫將在編譯時的最後一個引用該對象後插入。如果你沒有在初始化之後塊參照dict2,它會被編譯如下圖所示弧前的代碼:

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:這樣的類方法時,你(通常)會得到一個自動釋放對象。它將在運行循環結束時釋放。如果您在創建自動釋放字典後立即登錄,它仍然會在周圍並且有效。

+0

Thanks @DrummerB問題是用類方法創建的對象*不是*獲取autoreleased。我沒有顯示我的所有代碼。創建詞典後,我在NSNotificationCanter文章中使用它。另外,爲什麼Objective-C和Objective-C++之間的區別? – Android63

+0

好吧,發佈代碼的其餘部分,然後 – DrummerB

+0

@DrummerB:ARC實際上足夠聰明,能夠在許多情況下「捕獲」從類方法返回的自動釋放對象,以便它們不會進入autorelease池。 –