objc與鴨子物件(上)

發表於2017-01-16

這是《objc與鴨子物件》的上半部分,《objc與鴨子物件(下)》中介紹了鴨子型別的進階用法、依賴注入以及demo。

我是前言

1151530583jw1ejqkwtxr1dj20rs0ijgo7
鴨子型別(Duck Type)即:“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子”,換成程式猿語言就是:“當呼叫者知道這個物件能呼叫什麼方法時,管它這個物件到底是什麼類的例項呢”。本文對objc中的鴨子型別物件進行簡單探究,並用一個“只用一個類實現Json Entity”的小demo實踐下這個思路的魔力。進階篇請看下半部分。


objc與鴨子型別

id型別是個大鴨子

鴨子型別是動態語言的特性,編譯時並不決定函式呼叫關係,說白了所有的型別宣告都是給編譯器看的。objc在動態和靜態方面找到了不錯的平衡,既保留了嚴格的靜態檢查也沒破壞執行時的動態特性。
我們知道,向一個objc物件(或Class)發訊息,實際上就是沿著它的isa指標尋找真正函式地址,所以只要一個物件滿足下面的結構,就可以對它傳送訊息:

也就是熟知的id型別,objc在語言層面先天就支援了這個基本的鴨子型別,我們可以將任意一個物件強轉為id型別從而向它傳送訊息,就算它並不能響應這個訊息,編譯器也無從知曉。
正如這篇文章中對objc物件的簡短定義:The best definition for a Smalltalk or Objective-C "object" is "something that can respond to messages. object並非一定是某個特定型別的例項,只要它能響應需要的訊息就可以了。

從@interface到@protocol

正如objc先天支援的動態的id型別,@protocol為鴨子型別提供了編譯時的強型別檢查,實現了Cocoa中經典的鴨子型別使用場景:

利用鴨子型別設計的介面會給使用者更大的靈活度。同時@protocol可以用來建立偽繼承關係

協議的存在一方面是給NSProxy這樣的其他根類使用,同時也給了鴨子協議型別一個根型別,正如給了大部分類一個NSObject根類一樣。說個小插曲,由於objc中Class也是id型別,形如id的鴨子型別是可以用Class物件來扮演的,只需要把例項方法替換成類方法,如:

設定table view的data source:

這種非主流寫法合法且執行正常,歸功於objc中加號和減號方法在@selector中並未體現,在@protocol中也是形同虛設,這種程式碼我相信沒人真的寫,但確實能體現鴨子型別的靈活性。


[Demo]一個類實現Json Entity

Entity物件表示某個純資料的結構,如:

實際開發中這種類往往對應著server端返回的一個JSON串,如:

解析這些對映是個純重複工作,建類、寫屬性、解析…如今已經有JSONModelMantle等不錯的框架幫忙。這個demo我們要用鴨子型別的思想去重新設計,把這些Entity類簡化成一個鴨子類。

由於上面的UserEntity類,只有屬性的getter和setter,這正對應了NSMutableDictionaryobjectForKey:setObjectForKey:,同時,JSON資料也會解析成字典,這就完成了巧妙的對接,下面去實現這個類。

真正幹活的是一個字典,保證封裝性和純粹性,這個類直接使用NSProxy作為純代理類,只暴露一個初始化方法就好了:

NSProxy預設是沒有初始化方法的,也省去了去規避其他初始化方法的麻煩,為了簡單直接初始化時就把json串解開成字典(暫不考慮json是個array):

NSProxy可以說除了過載訊息轉發機制外沒有別的用法,這也是它被設計的初衷,自己什麼都不幹,轉給代理物件就好。往這個proxy發訊息是註定會走訊息轉發的,首先判斷下是不是一個getter或setter的selector:

簽名替換成字典的兩個方法後開始走轉發,在這裡設定引數和對內部字典的真正呼叫:

當然還有這兩個必不可少的從getter和setter中獲取屬性名的Helper:

一個簡單的鴨子Entity就完成了,之後所有的Entity都可以使用@protocol而非子類化的方式來定義,如:

當資料從網路層回來時,鴨子型別讓這個物件用起來和真有這麼個類沒什麼兩樣:

至此,所有的entity被表示成了N個.h檔案加一個XXDuckEntity類,剩下的就靠想象力了。
這個demo的原始碼將在下半部分之後給出


Reference

http://en.wikipedia.org/wiki/Duck_typing
http://www.informit.com/articles/article.aspx?p=1353396
https://github.com/facebook/facebook-ios-sdk

相關文章