-
[receiver message]
-
objc_msgSend(receiver, selector, arg1, arg2, ...)
-
id objc_msgSend ( id self, SEL op, ... );
-
typedef struct objc_object *id; typedef struct objc_selector *SEL;
-
struct objc_object {Class isa;};
-
typedef struct objc_class *Class;
從[receiver message]順藤摸瓜 我們能夠找到objc_class的結構體的指標,objc_class就是我們摸到的瓜,裡面關聯了它的超類,類名,成員變數,方法,快取,還有附屬協議。
在objc_class結構體中,ivars是objc_ivar_list指標;methodLists是指向objc_method_list指標的指標。可以動態修改*methodLists的值來新增成員方法,也就是Category的實現原理,解釋了為什麼不能新增屬性的原因。任性的話可以在Category中新增@dynamic的屬性,並利用執行期動態提供存取方法或乾脆動態轉發;或者乾脆使用關聯度物件(AssociatedObject)。
其中objc_ivar_list和objc_method_list 分別是成員變數列表和方法列表:
objc_ivar_list 儲存了objc_ivar,每個objc_ivar都儲存了個類的單個成員變數的資訊。同理objc_method_list儲存了objc_method,每個objc_method儲存了類的方法的資訊。
不知道你是否注意到了objc_class中也有一個isa物件,這是因為一個 ObjC 類本身同時也是一個物件,為了處理類和物件的關係,runtime 庫建立了一種叫做元類 (Meta Class) 的東西,類物件所屬型別就叫做元類,它用來表述類物件本身所具備的後設資料。類方法就定義於此處,因為這些方法可以理解成類物件的例項方法。每個類僅有一個類物件,而每個類物件僅有一個與之相關的元類。當你發出一個類似[NSObject alloc]的訊息時,你事實上是把這個訊息發給了一個類物件 (Class Object) ,這個類物件必須是一個元類的例項,而這個元類同時也是一個根元類 (root meta class) 的例項。所有的元類最終都指向根元類為其超類。所有的元類的方法列表都有能夠響應訊息的類方法。所以當[NSObject alloc]這條訊息發給類物件的時候,objc_msgSend()會去它的元類裡面去查詢能夠響應訊息的方法,如果找到了,然後對這個類物件執行方法呼叫。根元類的超類是NSObject,而isa指向了自己,而NSObject的超類為nil,也就是它沒有超類。