2014-10-20 48 views
4

TestObj類是一個簡單的類,它有一個方法doSomethingInBackground我發送它performSelectorInBackground使自己在後臺線程中休眠5秒的方法。發送消息時會自動保留

@implementation TestObj 

- (void)dealloc 
{ 
    NSLog(@"%@, is main thread %u", NSStringFromSelector(_cmd), [NSThread isMainThread]) ; 
} 

- (void)doSomethingInBackground 
{ 
    [self performSelectorInBackground:@selector(backgroundWork) withObject:nil] ; 
} 

- (void)backgroundWork 
{ 
    sleep(5) ; 
} 

@end 

我alloc和初始化實例,並將其發送doSomethingInBackground消息,並指派nil它以儘快將其釋放。

TestObj *obj = [[TestObj alloc] init] ; 
[obj doSomethingInBackground] ; 
obj = nil ; 

我發現dealloc將約5秒後obj = nil;運行,似乎是系統保留self時發送的方法[self performSelectorInBackground:@selector(backgroundWork) withObject:nil] ;backgroundWork返回後,該實例將被釋放。

任何人都可以告訴我係統在這個背後做的工作。謝謝。

回答

2

-[NSObject performSelectorInBackground:withObject:]引擎蓋下稱-[NSThread initWithTarget:selector:object:]這確實保留了原有的接收器(如target參數這裏傳遞)

NSThread文檔:「這些對象的目標和論證的分離線程的執行過程中被保留,他們被釋放時線程終於退出了。「

+0

如果我改變'[self performSelectorInBackground:@selector(backgroundWork)withObject:nil]''self performSelector:@selector(backgroundWork)withObject:nil afterDelay:5]'並且'backgroundWork'方法中什麼也不做,結果是一樣的。所以我認爲這不是主要原因。 – KudoCC 2014-10-20 07:01:36

+1

@KudoCC' - [NSObject performSelector:withObject:afterDelay:]'直接保留接收器。在調用選擇器之後,接收器通過'CFRunLoopTimerInvalidate'釋放。所以我假設' - [NSObject performSelector:withObject:afterDelay:]'調度計時器來延遲方法調用。計時器保留目標。您可以通過禁用ARC,在'TestObj'中重載'-retain'和'-release'方法並在其中設置斷點來輕鬆調查此問題。 (你可以運行這個https://gist.github.com/bartekchlebek/c36dba5d41bbe7b6fb5e) – 2014-10-20 07:13:16

+0

聽起來很合理,我會試一試,謝謝。 – KudoCC 2014-10-20 07:21:59

2

從文檔,

performSelectorInBackground:withObject:方法創建一個新的分離線程,並使用指定的方法爲切入點新線程。例如,如果你有一些對象(由變量MyObj中表示)和對象有一個名爲DoSomething的方法要在後臺線程中運行,你可以使用下面的代碼來做到這一點:

[myObj performSelectorInBackground:@selector(doSomething) withObject:nil]; 

調用此方法的效果與您將當前對象,選擇器和參數對象作爲參數調用NSThreaddetachNewThreadSelector:toTarget:withObject:方法相同。新線程立即使用默認配置生成並開始運行。

在detachNewThreadSelector:toTarget:withObject:文檔,

aTarget和anArgument分離的線程的執行期間 保留的對象,然後被釋放。一旦目標已經完成 執行aSelector方法,脫離的線程退出 (使用退出類方法)。

關於performSelector:AfterDelay:,

此方法設置了一個定時器來執行對 當前線程的運行循環的aSelector消息。定時器配置爲以默認模式(NSDefaultRunLoopMode)運行於 。當定時器觸發時,線程 嘗試從運行循環中出列消息並執行 選擇器。如果運行循環正在運行並且處於默認模式 ,則成功;否則,計時器等待直到運行循環是在默認模式

而且

計時器維護強引用此對象,直到它( 定時器)被無效。

如果您不希望您的OBJ得以保留,你可以使用弱引用的對象呢,

TestObj *obj = [[TestObj alloc] init] ; 
__weak typeof(obj) weakObj = obj; 
[weakObj doSomethingInBackground] ; 
obj = nil ; 
+0

你是不是指'[weakObj doSometingInBackground]'?目前,創建'weakObj'沒有太大意義,因爲它沒有被使用。 – 2014-10-20 07:29:09

+0

哎呀,謝謝你指出。 – akshaynhegde 2014-10-20 07:29:59

+0

不客氣! – 2014-10-20 07:31:05

1

在特殊情況下,像performSelector:已經在其他的答案被討論,我認爲這是有幫助的添加回答一般情況下:

會自行保留時發送消息給它

否。在手動保留計數和ARC self從不隱式保留。在執行方法期間,您必須注意消息的接收者未被釋放。

雖然根據ARC self的類型很強,但實際上並未保留該對象。請參閱ARC documentation

+0

感謝一般情況下的答案和非常有用的文檔。 – KudoCC 2014-10-20 08:00:31