在 iOS 中實現方法鏈呼叫

林欣達發表於2017-04-14

前言

鏈式呼叫(chained calls)是指在函式呼叫返回了一個物件的時候,使得這個呼叫鏈可以不斷的呼叫下去。從概念上可以看做是一環扣一環的鐵鏈,也能被稱作方法鏈呼叫。

在 iOS 中實現方法鏈呼叫

假設需求是在網路請求完成之後先篩選過期資料,然後轉換成對應的資料模型進行展示。在Swift中可以直接這麼寫:

OC的語法決定了這步操作不能像Swift一樣簡潔,最常見的程式碼就是這樣:

對比兩段程式碼,不難看出方法鏈呼叫的優點包括:

  • 程式碼簡潔優雅,可讀性強
  • 減少了重複使用同一變數的程式碼量
  • 把複雜的操作分割成多個小操作連續呼叫

Swift的特性決定了其在鏈式呼叫上的先天優勢,但是有沒有辦法讓OC也能擁有這種鏈式呼叫的特性呢?答案是毫無疑問的,block是一種非常優秀的機制,允許我們使用點語法的方式呼叫屬性block

其他要求

實現鏈式呼叫做法並不複雜,但是符合這些要求會讓你用起來更加得心應手。譬如:

  • block有過足夠深的使用和了解
  • retain cycle深惡痛疾,網上很多教程實際上存在著迴圈引用的問題
  • 升級到Xcode8.3以上的版本,理由無他,加強了屬性block呼叫的程式碼聯想

其中第三點是筆者最推崇的要求,要知道8.3之前的鏈式封裝多多少少吃了不少程式碼聯想的苦頭

醜陋的資料來源

UITableView是個非常牛逼的控制元件,但是對於開發者而言也並不是那麼的友善,甚至有些醜陋。實現一次tableView的代理起碼要有以下程式碼:

Protocol是一種非常優雅的設計方案,這點是毋庸置疑的。但即便再優雅的程式碼設計,在重複的程式碼面前,也會讓人感到醜陋、厭倦。

block相較起代理,是一種更加強大的機制。比前者更解耦更簡潔,當然兩者的優劣比較不是本文要討論的問題,通過block呼叫的返回物件,大可以給NSArray實現這樣的程式碼:

雖然程式碼簡潔性上仍然差了Swift一籌,但是相較起原執行程式碼邏輯性跟可讀性都強了不少,這部分的程式碼詳見由淺至深學習block

鏈式資料來源的實現

由於誰都可能是UITableView的資料來源物件,那麼最粗暴的做法是為NSObject提供一個category用來實現相關的資料來源並且動態新增屬性。但是作為有追求的攻城獅,我們有追求,要優雅,所以這種方式直接cut

UITableView的繁雜程式碼一直是熱議的話題之一,在不同的程式碼架構中有不同的解決方案,比如MVVM中封裝一個專門的VM來統一處理這部分邏輯。因此可以提供一個物件作為實際資料來源物件跟UITableView之間的中介。由中介實現相關協議方法,然後從實際資料來源物件方獲取資料

在 iOS 中實現方法鏈呼叫

中介被我命名為LXDTableViewProtocolHelper,為了保證能夠生成方法鏈,所有的屬性block應當返回中介物件:

typedef LXDTableViewProtocolHelper (^TVBindAndRegister)(UITableView tableView, Class cellCls, BOOL isNib, NSString * reuseIdentifier);

使用只讀屬性修飾block之後我們只需重寫getter方法返回對應的處理就行了,拿rowsNumber做個例子,按照網上很多教程都會這麼寫:

但是實際上這個返回的block__NSMallocBlock__型別的,這意味著這種做法會讓中介被引用。當然筆者沒去測試中介是否能正確釋放而是直接採用__weak做法,感興趣的讀者可以重寫dealloc來檢測。最後tableView的協議方法就能被這樣實現:

更多

得益於強大的block,即便OC沒有Swift那麼優雅的高階函式,依舊能夠實現讓程式碼緊湊已讀,當然也會提高debug的難度。除了將資料來源鏈式之外,你還可以嘗試把網路請求進行封裝,做成鏈式處理,比如筆者的請求程式碼:

相關文章