# Consumed parameters
consumed這個單詞我并不能給出很準確的翻譯,在這篇文章中,我把Consumed parameters稱為耗用參數,它在OC中有著獨特的應用場景。
在https://clang.llvm.org/docs/AutomaticReferenceCounting.html#id7這份文檔中,講解了ARC方面的知識,我對Consumed parameters這一個小模塊有很大的疑問,因此在網上查了一些資料,雖然有了一個大概的了解,但是還是有一些不太清楚的地方。
我們先來看一個例子,這個例子來源于上邊的那份文檔:
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操作,這一過程很像函數對局部變量的操作。
到這里就產生了第一個疑問?
為什么要對傳入的參數做retain,在結束時又release掉?
這個跟參數的生命周期有關,我們在函數中使用了參數,當然希望能夠得到這個參數的所有權,并且希望該參數一直存活著。這個內容我會在下邊的內容中給出一定的解釋。在上邊的文檔中有這樣一段話:
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的時候,需要注意一下幾點:
- 保證方法的接收者不能為null,因為在方法被調用之前,參會會做retain操作,這樣就帶來了內存泄漏的問題
- 傳遞的參數的個數不能大于方法能夠動態處理的個數,否則可能引起未知的后果
- 謹慎處理靜態類型的問題
何為靜態類型,何為動態類型?
A *a = [A new]; B *b = a;那么b的靜態類型就是B,這個類型是由編譯器決定的,而A則是它的動態類型,由運行時決定。
我發現ASDisplayKit的源碼極其復雜,估計要花相當多的時間來解讀了。不能放棄,加油。
總結
以上是生活随笔為你收集整理的# Consumed parameters的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 状态码
- 下一篇: Pandas.plot 做图 demo(