RunTime簡(jiǎn)稱運(yùn)行時(shí)。就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。OC的函數(shù)調(diào)用成為消息發(fā)送。屬于動(dòng)態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明,在編 譯階段,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要申明過就不會(huì)報(bào)錯(cuò)。)只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找 到對(duì)應(yīng)的函數(shù)來調(diào)用。
1.下面我們來看最基本的類的定義
Class實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針
class的定義:
typedef struct objc_class *Class;
class 是一個(gè)objc_class 結(jié)構(gòu)類型的指針,id是一個(gè)objc_class類型的指針
objc_class
struct objc_class {
struct objc_class* isa;
struct objc_class* super_class;
const char* name;
long version;
long info;
long instance_size;
struct objc_ivar_list* ivars;
struct objc_method_list** methodLists;
struct objc_cache* cache;
struct objc_protocol_list* protocols;
};
也就是說class的結(jié)構(gòu)是上面那樣的,objc_class的成員介紹如下:
isa:是一個(gè)objc_class類型的指針,其實(shí)是指向元類metaclass。
super_class : 一個(gè)指向父類的objc_class類型的指針,如果是最頂層,那么super_class 為 null
在繼承關(guān)系中,通常會(huì)有,子類,父類,根類,他們的對(duì)應(yīng)關(guān)系通常是這樣的
類的實(shí)例對(duì)象的isa指向該類,該類的isa指向該類的父類,類的super_class指向其父類,如此一層一層指向

class和metaclass
class是instance object 的類類型 。當(dāng)我們向?qū)嵗龑?duì)象發(fā)送消息(實(shí)例方法 )的時(shí)候,我們?cè)谠搶?shí)例對(duì)象的class的方法列表(methodlists )中查找相應(yīng)函數(shù),如果沒有找到就去改類的父類的方法列表中查找,然后一層一層去查找,直到找到為止。
NSString * str;[str lowercaseString];
向 str 實(shí)例對(duì)象發(fā)送 lowercaseString 消息,會(huì)在 NSString 類結(jié)構(gòu)的 methodlists 中去查找 lowercaseString 的響應(yīng) 函數(shù)。
metaclass是class object 類類型。當(dāng)我們向類對(duì)象發(fā)送消息(類方法) 的時(shí)候,我們?cè)谠擃悓?duì)象的 metaclass 結(jié)構(gòu)的 methodlists 中去查找響應(yīng)的函數(shù),如果沒有找到匹配的響應(yīng)函數(shù)則在該 metaclass 的父類中的 methodlists 去查找
[NSString stringWithString:@"str"];
向 NSString 類對(duì)象發(fā)送 stringWithString 消息,會(huì)在 NSString 的 metaclass 類結(jié)構(gòu)的 methodlists 中去查找 stringWithString 的響應(yīng)函數(shù)
name:表示類的名稱,通過這個(gè)名稱查找到該類(通過:id objc_getClass(const char *aClassName))或該類的 metaclass(id objc_getMetaClass(const char *aClassName));
version:類的版本信息,可以在運(yùn)行期對(duì)其進(jìn)行修改(class_setVersion)或獲 取(class_getVersion)。
info:供運(yùn)行期使用的一些位標(biāo)識(shí)。
有如下一些位掩碼:CLS_CLASS (0x1L) 表示該類為普通 class ,其中包含實(shí)例方法和變量;
CLS_META (0x2L) 表示該類為 metaclass,其中包含類方法;
CLS_INITIALIZED(0x4L) 表示該類已經(jīng)被運(yùn)行期初始化了,這個(gè)標(biāo)識(shí)位只被 objc_addClass 所設(shè)置;
CLS_POSING (0x8L) 表示該類被 pose 成其他的類;(poseclass 在 ObjC 2.0 中被廢棄了);
CLS_MAPPED (0x10L) 為 ObjC 運(yùn)行期所使用CLS_FLUSH_CACHE (0x20L) 為 ObjC 運(yùn)行期所使用
CLS_GROW_CACHE (0x40L) 為 ObjC 運(yùn)行期所使用
CLS_NEED_BIND (0x80L) 為 ObjC 運(yùn)行期所使用
CLS_METHOD_ARRAY (0x100L) 該標(biāo)志位指示 methodlists 是指向一個(gè)objc_method_list 還是 一個(gè)包含 objc_method_list 指針的數(shù)組;
instance_size:該類的實(shí)例變量大小(包括從父類繼承下來的實(shí)例變量);
ivars: 指向objc_ivar_list 的指針 ,儲(chǔ)存每個(gè)實(shí)例變量的地址,如果沒有的話是null
objc_method_list:方法列表
cache:方法的緩存
protocols:協(xié)議列表
2.對(duì)象
objc_object 與id
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
可以看出,objc_object結(jié)構(gòu)體里只有一個(gè)字段,isa指針,當(dāng)我們向一個(gè)obj對(duì)象發(fā)送消息的是時(shí)候,runtime會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所對(duì)應(yīng)的類,然后去類的方法列表里找到對(duì)應(yīng)的方法。
Id類型也是一個(gè)objc_object的結(jié)構(gòu)類型的指針。
- objc_cache
objc_cache結(jié)構(gòu)體定義如下:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
1.mask:是一個(gè)int類型整數(shù),指定分配緩存的bucket的總數(shù),在方法查找的過程中,runtime使用這個(gè)字段來確定開始線性查找數(shù)組的索引位置??梢宰鳛橐粋€(gè)簡(jiǎn)單的hash算法
2.occupied:一個(gè)int類型整數(shù),指定實(shí)際占用緩存的bucket的總數(shù)
3.buckets:指向method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組的指針,這個(gè)數(shù)組可能為null,表示這個(gè)緩存bucket沒有占用。
4.關(guān)于metaclass(元類):
meta-class是一個(gè)類對(duì)象的類。
當(dāng)我們向一個(gè)對(duì)象發(fā)送消息的時(shí)候,runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法,如果我們向一個(gè)類發(fā)送消息的時(shí)候,會(huì)在這個(gè)類的meta-class的方法列表中查找。
Meta-class儲(chǔ)存著一個(gè)類的所有的類方法。
再深入一下,meta-class也是一個(gè)類,也可以向它發(fā)送一個(gè)消息,那么它的isa又是指向什么呢?為了不讓這種結(jié)構(gòu)無限延伸下去,Objective-C的設(shè)計(jì)者讓所有的meta-class的isa指向基類的meta-class,以此作為它們的所屬類。即,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己。這樣就形成了一個(gè)完美的閉環(huán)。
5.類和對(duì)象的runtime函數(shù)總結(jié)
(1)類
類名:
// 獲取類的類名
const char * class_getName ( Class cls );
對(duì)于class_getName函數(shù),如果傳入的cls為Nil,則返回一個(gè)字字符串。
父類和元類
// 獲取類的父類
Class class_getSuperclass ( Class cls );
// 判斷給定的Class是否是一個(gè)元類
BOOL class_isMetaClass ( Class cls );
class_getSuperclass函數(shù),當(dāng)cls為Nil或者cls為根類時(shí),返回Nil。不過通常我們可以使用NSObject類的superclass方法來達(dá)到同樣的目的。
class_isMetaClass函數(shù),如果是cls是元類,則返回YES;如果否或者傳入的cls為Nil,則返回NO。
實(shí)例變量大小(instance_size)
// 獲取實(shí)例大小
size_t class_getInstanceSize ( Class cls );
成員變量(ivars)和屬性
在obj_class中,所有的成員變量和屬性的信息都是放在 struct objc_ivar_list* ivars中的,ivars是一個(gè)數(shù)組,數(shù)組中每個(gè)元素都是指向ivar的指針。
1.成員變量操作函數(shù)
// 獲取類中指定名稱實(shí)例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 獲取類成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
// 獲取整個(gè)成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
class_getInstanceVariable函數(shù),它返回一個(gè)指向包含name指定的成員變量信息的objc_ivar結(jié)構(gòu)體的指針(Ivar)。
class_getClassVariable函數(shù),目前沒有找到關(guān)于Objective-C中類變量的信息,一般認(rèn)為Objective-C不支持類變量。注意,返回的列表不包含父類的成員變量和屬性。
Objective-C不支持往已存在的類中添加實(shí)例變量,因此不管是系統(tǒng)庫提供的提供的類,還是我們自定義的類,都無法動(dòng)態(tài)添加成員變量。但如果我們通過運(yùn)行時(shí)來創(chuàng)建一個(gè)類的話,又應(yīng)該如何給它添加成員變量呢?這時(shí)我們就可以使用class_addIvar函數(shù)了。不過需要注意的是,這個(gè)方法只能在objc_allocateClassPair函數(shù)與objc_registerClassPair之間調(diào)用。另外,這個(gè)類也不能是元類。成員變量的按字節(jié)最小對(duì)齊量是1<<alignment。這取決于ivar的類型和機(jī)器的架構(gòu)。如果變量的類型是指針類型,則傳遞log2(sizeof(pointer_type))。
class_copyIvarList函數(shù),它返回一個(gè)指向成員變量信息的數(shù)組,數(shù)組中每個(gè)元素是指向該成員變量信息的objc_ivar結(jié)構(gòu)體的指針。這個(gè)數(shù)組不包含在父類中聲明的變量。outCount指針返回?cái)?shù)組的大小。需要注意的是,我們必須使用free()來釋放這個(gè)數(shù)組。
2.屬性操作
// 獲取指定的屬性
objc_property_t class_getProperty ( Class cls, const char *name );
// 獲取屬性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
// 為類添加屬性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
// 替換類的屬性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
方法methodLists
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 獲取實(shí)例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實(shí)現(xiàn)
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類實(shí)例是否響應(yīng)指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
class_addMethod的實(shí)現(xiàn)會(huì)覆蓋父類的方法實(shí)現(xiàn),但不會(huì)取代本類中已存在的實(shí)現(xiàn),如果本類中包含一個(gè)同名的實(shí)現(xiàn),則函數(shù)會(huì)返回NO。如果要修改已存在實(shí)現(xiàn),可以使用method_setImplementation。一個(gè)Objective-C方法是一個(gè)簡(jiǎn)單的C函數(shù),它至少包含兩個(gè)參數(shù)—self和_cmd。所以,我們的實(shí)現(xiàn)函數(shù)(IMP參數(shù)指向的函數(shù))至少需要兩個(gè)參數(shù)
void myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
與成員變量不同的是,我們可以為類動(dòng)態(tài)添加方法,不管這個(gè)類是否已存在
lass_getInstanceMethod、class_getClassMethod函數(shù),與class_copyMethodList不同的是,這兩個(gè)函數(shù)都會(huì)去搜索父類的實(shí)現(xiàn)。
class_copyMethodList函數(shù),返回包含所有實(shí)例方法的數(shù)組,如果需要獲取類方法,則可以使用class_copyMethodList(object_getClass(cls), &count)(一個(gè)類的實(shí)例方法是定義在元類里面)。該列表不包含父類實(shí)現(xiàn)的方法。outCount參數(shù)返回方法的個(gè)數(shù)。在獲取到列表后,我們需要使用free()方法來釋放它。
class_replaceMethod函數(shù),該函數(shù)的行為可以分為兩種:如果類中不存在name指定的方法,則類似于class_addMethod函數(shù)一樣會(huì)添加方法;如果類中已存在name指定的方法,則類似于method_setImplementation一樣替代原方法的實(shí)現(xiàn)。
class_getMethodImplementation函數(shù),該函數(shù)在向類實(shí)例發(fā)送消息時(shí)會(huì)被調(diào)用,并返回一個(gè)指向方法實(shí)現(xiàn)函數(shù)的指針。這個(gè)函數(shù)會(huì)比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函數(shù)指針可能是一個(gè)指向runtime內(nèi)部的函數(shù),而不一定是方法的實(shí)際實(shí)現(xiàn)。例如,如果類實(shí)例無法響應(yīng)selector,則返回的函數(shù)指針將是運(yùn)行時(shí)消息轉(zhuǎn)發(fā)機(jī)制的一部分。
class_respondsToSelector函數(shù),我們通常使用NSObject類的respondsToSelector:或instancesRespondToSelector:方法來達(dá)到相同目的。
協(xié)議(objc_protocol_list)
// 添加協(xié)議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回類是否實(shí)現(xiàn)指定的協(xié)議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回類實(shí)現(xiàn)的協(xié)議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
class_conformsToProtocol函數(shù)可以使用NSObject類的conformsToProtocol:方法來替代。
class_copyProtocolList函數(shù)返回的是一個(gè)數(shù)組,在使用后我們需要使用free()手動(dòng)釋放。
版本(version)
// 獲取版本號(hào)
int class_getVersion ( Class cls );
// 設(shè)置版本號(hào)
void class_setVersion ( Class cls, int version );