consumed這個單詞我並不能給出很準確的翻譯,在這篇文章中,我把Consumed parameters稱爲耗用參數,它在OC中有着獨特的應用場景。html
在https://clang.llvm.org/docs/AutomaticReferenceCounting.html#id7這份文檔中,講解了ARC方面的知識,我對Consumed parameters這一個小模塊有很大的疑問,所以在網上查了一些資料,雖然有了一個大概的瞭解,可是仍是有一些不太清楚的地方。ide
咱們先來看一個例子,這個例子來源於上邊的那份文檔:函數
void foo(__attribute((ns_consumed)) id x); - (void) foo: (id) __attribute((ns_consumed)) x;
咱們能夠用__attribute((ns_consumed))
來修飾某個函數或者方法的參數,但這只是表面上的見解,實際上,它並非只做用於它修飾的某個參數,而是做用於整個函數或方法。優化
它的一個限制是,只能修飾可retain的對象指針類型,好比id
, Class
等等,不能修飾int *
這樣的類型。翻譯
上邊的兩行代碼表示foo
被標記爲consumed。意味着該函數的被調用者但願獲得一個+1 retain count的對象。聲明瞭這個屬性後,當傳入一個參數時,在函數調用前,ARC會把對該參數作一次retain操做,在該函數結束後再對該參數作一次release操做,這一過程很像函數對局部變量的操做。指針
到這裏就產生了第一個疑問?code
爲何要對傳入的參數作retain,在結束時又release掉?orm
這個跟參數的生命週期有關,咱們在函數中使用了參數,固然但願可以獲得這個參數的全部權,而且但願該參數一直存活着。這個內容我會在下邊的內容中給出必定的解釋。在上邊的文檔中有這樣一段話:htm
Rationale This formalizes direct transfers of ownership from a caller to a callee. The most common scenario here is passing the self parameter to init, but it is useful to generalize. Typically, local optimization will remove any extra retains and releases: on the caller side the retain will be merged with a +1 source, and on the callee side the release will be rolled into the initialization of the parameter.
這段話指出,上邊的操做直接從調用者到被調用者轉移了全部權,最多見的一個場景就是傳遞self
參數到init
方法之中,這個內容將是本文最重要的內容。通常來講,局部的優化會移除任何額外的retain和release操做,這句話的意思是說,在函數中,某些局部變量不必定都會十分嚴格的按照retain/release原則來進行操做。調用端將會進行一些必要的合併操做,而被調用端也會對參數作一些額外的操做。對象
到這裏,有了第二個疑問?
在ARC中,爲何self
在init
方法中是一個consumed parameter?
這個問題我以前是不知道的,它來源於這個提問。init
方法被標記爲ns_consumes_self
。ns_consumes_self
說明在方法中遵循上邊講的原則,在方法調用以前先把self
作retain操做,結束時作release操做。
User *user = [[User alloc] init];
這是一行很是簡單的代碼,在調用了alloc
後就建立了一個User對象,這個能夠在這篇回答中得到證據。返回的對象的retain count等於1,你們應該記得,凡是經過alloc/new/copy.etc
生成的對象,retain count都會+1,那麼在這裏的init
方法中:
self = [super init]; if (self) { ... } return self;
self
首先被init
的調用者作了一次retain操做,此時它的retain count爲1,執行完self = [super init];
後,它的retain count爲2,直到init
返回後,self
作了一次release操做,此時它的retain count爲1。**這就完美保證了self
在方法中是一直存活的,也保證了可以返回一個retain count爲1的對象。
有興趣能夠翻看這個提問中的回答的部分,那哥們說的很詳細,再說一點,在之前的MRC時代,代碼能夠這樣寫:
- (NSView *)view { //explicit retain-autorelease of +1 variable is +2 -> +1, guaranteed access or nil. return [[_view retain]autorelease]; }
爲了正確返回某個對象,先retain再release。
所以在使用consumed的時候,須要注意一下幾點:
何爲靜態類型,何爲動態類型?
A *a = [A new]; B *b = a;
那麼b的靜態類型就是B,這個類型是由編譯器決定的,而A則是它的動態類型,由運行時決定。
我發現ASDisplayKit的源碼極其複雜,估計要花至關多的時間來解讀了。不能放棄,加油。