# Consumed parameters

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中,爲何selfinit方法中是一個consumed parameter?

這個問題我以前是不知道的,它來源於這個提問init方法被標記爲ns_consumes_selfns_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的時候,須要注意一下幾點:

  • 保證方法的接收者不能爲null,由於在方法被調用以前,參會會作retain操做,這樣就帶來了內存泄漏的問題
  • 傳遞的參數的個數不能大於方法可以動態處理的個數,不然可能引發未知的後果
  • 謹慎處理靜態類型的問題

何爲靜態類型,何爲動態類型?

A *a = [A new];
B *b = a;

那麼b的靜態類型就是B,這個類型是由編譯器決定的,而A則是它的動態類型,由運行時決定。

我發現ASDisplayKit的源碼極其複雜,估計要花至關多的時間來解讀了。不能放棄,加油。

相關文章
相關標籤/搜索