1.OC的類和對象
先看一下類和對象的數(shù)據(jù)結(jié)構(gòu):
typedef struct objc_class *Class;//類對象
struct objc_class {//類對象的結(jié)構(gòu)體
Class isa? OBJC_ISA_AVAILABILITY;//meta-class里存儲類方法促煮,meta-class的isa指向根類,super_class指向父類的meta-class整袁。
#if !__OBJC2__
Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
const char *name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
long version? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
long info? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
long instance_size? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
struct objc_cache *cache? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
struct objc_object {//實(shí)例對象結(jié)構(gòu)體
Class isa? OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;//實(shí)例對象
每個(gè)對象都會有一個(gè)它所屬的類菠齿。這是面向?qū)ο蟮幕靖拍睿窃贠C中坐昙,這對所有數(shù)據(jù)結(jié)構(gòu)有效绳匀。任何數(shù)據(jù)結(jié)構(gòu),只要在恰當(dāng)?shù)奈恢镁哂幸粋€(gè)指針指向一個(gè)class炸客,那么疾棵,它都可以被認(rèn)為是一個(gè)對象。objc_class和objc_object的struct指針都能稱為對象痹仙,我們?yōu)榱藚^(qū)分可以把它們分別叫做類對象和實(shí)例對象是尔,其中實(shí)例對象的isa指針指向類對象。
在OC中蝶溶,一個(gè)對象所屬于哪個(gè)類嗜历,是由它的isa指針指向的。這個(gè)isa指針指向這個(gè)對象所屬的class抖所。
我們看一下類對象的結(jié)構(gòu)體objc_class:Class isa指向的就是metaclass梨州,當(dāng)我們向一個(gè)類發(fā)送消息的時(shí)候,就是調(diào)用metaclass里面的方法田轧,如[NSString defaultStringEncoding]暴匠,Class super_class指向父類。同樣metaclass的結(jié)構(gòu)體也是objc_class傻粘,它的Class isa指向直接指向根類的metaclass每窖,根類的metaclass則是指向自身;它的Class super_class指向父類的metaclass弦悉;super_class的結(jié)構(gòu)體也是objc_class窒典,它的Class isa指向也是直接指向根類的metaclass,如果父類也有一個(gè)父類稽莉,它的Class super_class就是想父類瀑志。
類對象的結(jié)構(gòu)體objc_class里面用Class super_class指向父類是為了實(shí)現(xiàn)繼承;Class isa指向的就是metaclass則是為了實(shí)現(xiàn)向類發(fā)消息(也就是類方法)污秆,同時(shí)讓類和對象有了區(qū)分劈猪。
為了驗(yàn)證以上所述,我新建一個(gè)類Yordles繼承NSObject良拼,然后再新建一個(gè)類Timor繼承Yordles战得,用clang rewrite成c的實(shí)現(xiàn)
Timor類截取主要代碼如下:
static void OBJC_CLASS_SETUP_$_Timor(void ) {
OBJC_METACLASS_$_Timor.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Timor.superclass = &OBJC_METACLASS_$_Yordles;
OBJC_METACLASS_$_Timor.cache = &_objc_empty_cache;
OBJC_CLASS_$_Timor.isa = &OBJC_METACLASS_$_Timor;
OBJC_CLASS_$_Timor.superclass = &OBJC_CLASS_$_Yordles;
OBJC_CLASS_$_Timor.cache = &_objc_empty_cache;
}
Yordles類截取相關(guān)代碼如下:
static void OBJC_CLASS_SETUP_$_Yordles(void ) {
OBJC_METACLASS_$_Yordles.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Yordles.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Yordles.cache = &_objc_empty_cache;
OBJC_CLASS_$_Yordles.isa = &OBJC_METACLASS_$_Yordles;
OBJC_CLASS_$_Yordles.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_Yordles.cache = &_objc_empty_cache;
}
2.objc_msgSend
在我們傳消息的時(shí)候(比如[NSString defaultStringEncoding],[timor wait])庸推,在c層面一般都會轉(zhuǎn)化成objc_msgSend方法常侦。在討論objc_msgSend的工作原理前,我們先看幾個(gè)結(jié)構(gòu)體
Method
Method表示類中的某個(gè)方法予弧,在runtime.h文件中找到它的定義:
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
struct objc_method {
SEL method_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
char *method_types? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
IMP method_imp? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
}
其實(shí)Method就是一個(gè)指向objc_method結(jié)構(gòu)體指針刮吧,它存儲了方法名(method_name)、方法類型(method_types)和方法實(shí)現(xiàn)(method_imp)等信息掖蛤。而method_imp的數(shù)據(jù)類型是IMP杀捻,它是一個(gè)函數(shù)指針,后面會重點(diǎn)提及蚓庭。
Ivar
Ivar表示類中的實(shí)例變量致讥,在runtime.h文件中找到它的定義:
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
char *ivar_type? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
int ivar_offset? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
#endif
}
Ivar其實(shí)就是一個(gè)指向objc_ivar結(jié)構(gòu)體指針,它包含了變量名(ivar_name)器赞、變量類型(ivar_type)等信息垢袱。
IMP
在上面講Method時(shí)就說過,IMP本質(zhì)上就是一個(gè)函數(shù)指針港柜,指向方法的實(shí)現(xiàn)请契,在objc.h找到它的定義:
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
當(dāng)你向某個(gè)對象發(fā)送一條信息咳榜,可以由這個(gè)函數(shù)指針來指定方法的實(shí)現(xiàn),它最終就會執(zhí)行那段代碼爽锥,這樣可以繞開消息傳遞階段而去執(zhí)行另一個(gè)方法實(shí)現(xiàn)涌韩。
Cache
顧名思義,Cache主要用來緩存氯夷,那它緩存什么呢臣樱?我們先在runtime.h文件看看它的定義:
typedef struct objc_cache *Cache? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
struct objc_cache {
unsigned int mask /* total = mask + 1 */? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
unsigned int occupied? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
Method buckets[1]? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
};
Cache其實(shí)就是一個(gè)存儲Method的鏈表,主要是為了優(yōu)化方法調(diào)用的性能腮考。當(dāng)對象receiver調(diào)用方法message時(shí)雇毫,首先根據(jù)對象receiver的isa指針查找到它對應(yīng)的類,然后在類的methodLists中搜索方法踩蔚,如果沒有找到棚放,就使用super_class指針到父類中的methodLists查找,一旦找到就調(diào)用方法馅闽。如果沒有找到席吴,有可能消息轉(zhuǎn)發(fā),也可能忽略它捞蛋。但這樣查找方式效率太低孝冒,因?yàn)橥粋€(gè)類大概只有20%的方法經(jīng)常被調(diào)用,占總調(diào)用次數(shù)的80%拟杉。所以使用Cache來緩存經(jīng)常調(diào)用的方法庄涡,當(dāng)調(diào)用方法時(shí),優(yōu)先在Cache查找搬设,如果沒有找到穴店,再到methodLists查找
看了那么多元素概念,我們請出objc_msgSend在message.h中的定義:
/**
* Sends a message with a simple return value to an instance of a class.
*
* @param self A pointer to the instance of the class that is to receive the message.
* @param op The selector of the method that handles the message.
* @param ...
*? A variable argument list containing the arguments to the method.
*
* @return The return value of the method.
*
* @note When it encounters a method call, the compiler generates a call to one of the
*? functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
*? Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
*? other messages are sent using \c objc_msgSend. Methods that have data structures as return values
*? are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
上面的注釋翻譯過來就是:
當(dāng)編譯器遇到一個(gè)方法調(diào)用時(shí)拿穴,它會將方法的調(diào)用翻譯成以下函數(shù)中的一個(gè) `objc_msgSend`泣洞、`objc_msgSend_stret`、`objc_msgSendSuper` 和 `objc_msgSendSuper_stret`默色。
發(fā)送給對象的父類的消息會使用 `objc_msgSendSuper`
有數(shù)據(jù)結(jié)構(gòu)作為返回值的方法會使用 `objc_msgSendSuper_stret` 或 `objc_msgSend_stret`
其它的消息都是使用 `objc_msgSend` 發(fā)送的
現(xiàn)在讓我們看一下objc_msgSend它具體是如何發(fā)送消息:
首先根據(jù)receiver對象的isa指針獲取它對應(yīng)的class球凰;
優(yōu)先在class的cache查找message方法,如果找不到腿宰,再到methodLists查找呕诉;
如果沒有在class找到,再到super_class查找吃度;
一旦找到message這個(gè)方法甩挫,就執(zhí)行它實(shí)現(xiàn)的IMP。
你以為objc_msgSend到這里就結(jié)束了椿每,顯然不是伊者,這只是針對有方法實(shí)現(xiàn)的plan a英遭,當(dāng)objc_msgSend找不到實(shí)現(xiàn)的IMP時(shí),程序并不會直接崩潰亦渗,而是給開發(fā)者一個(gè)plan b:方法決議與消息轉(zhuǎn)發(fā)贪绘。這是有一組NSObject里的方法實(shí)現(xiàn)的,開發(fā)者只需要重寫響應(yīng)的方法即可央碟。具體流程如圖:
注意:第一步當(dāng)方法為實(shí)例方法時(shí)用:resolveInstanceMethod ,方法為類方法時(shí)用resolveClassMethod