本文是參考南峰子Objective-C Runtime系列文章
做一個(gè)自己的總結(jié),強(qiáng)烈推薦查看原文
1、runtime介紹
Objective-C 是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和連接時(shí)期做的事情放到了運(yùn)行時(shí)來處理,這種動(dòng)態(tài)語言的優(yōu)勢(shì)在于:我們寫代碼時(shí)更具有靈活性,如我們可以把消息轉(zhuǎn)發(fā)給我們想要轉(zhuǎn)發(fā)的對(duì)象,或者隨意交換一個(gè)方法等。
這種特性意味著Objective-C不僅需要一個(gè)編譯器,還需要一個(gè)運(yùn)行時(shí)動(dòng)態(tài)庫(kù)來執(zhí)行編譯的代碼。對(duì)于Objective-C來說,這個(gè)運(yùn)行時(shí)系統(tǒng)就像一個(gè)操作系統(tǒng)一樣:讓所有工作正常運(yùn)行。這個(gè)運(yùn)行時(shí)系統(tǒng)就是Objc Runtime ,Objc Runtime其實(shí)是一個(gè)Runtime庫(kù),基本上是用C語言和匯編語言寫的,使得C語言有了面向?qū)ο蟮哪芰Α?/p>
Runtime庫(kù)主要做了以下兩件事:
- 封裝:在這個(gè)庫(kù)中,對(duì)象可以使用
C語言中的結(jié)構(gòu)體表示,方法可以使用C函數(shù)來實(shí)現(xiàn),另外再加上一些額外的特性,這些結(jié)構(gòu)體和函數(shù)被runtime函數(shù)封裝后,我們就可以在程序運(yùn)行時(shí)創(chuàng)建、檢查、修改類、對(duì)象、方法了 - 找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行
[object doSomething]時(shí),會(huì)向接受者(object)發(fā)送一條消息(doSomething),runtime會(huì)根據(jù)消息接受者能否響應(yīng)該消息而做出不同反應(yīng)
Objective-C runtime目前有兩個(gè)版本:Modern runtime和Legacy runtime。Modern Runtime覆蓋了64位的Mac OS X Apps,還有iOS Apps,Legacy Runtime是早期用來給32位 Mac OS X Apps 用的,已經(jīng)過時(shí)
蘋果和GNU各自維護(hù)一個(gè)開源的 runtime 版本,這兩個(gè)版本之間都在努力的保持一致。
高級(jí)編程語言想要成為可執(zhí)行文件,需要先編譯成匯編語言,再匯編為機(jī)器語言,機(jī)器語言是計(jì)算機(jī)唯一能識(shí)別的語言(轉(zhuǎn)換過程待研究)
2、類
Objective-C類是有Class類型表示,實(shí)際上是指向objc_class結(jié)構(gòu)體的指針,定義如下
typedef struct objc_class *Class;
objc_class定義如下
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
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; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
1.isa : 需要注意的是在Objective-C中,所有的類自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針,它指向metaClass(元類)
super_class:指向該類的父類,如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy),則super_class為NULLname: 類名version:我們可以使用這個(gè)字段來提供類的版本信息。這對(duì)于對(duì)象的序列化非常有用,它可是讓我們識(shí)別出不同類定義版本中實(shí)例變量布局的改變。info: 類信息instance_size: 該類的對(duì)象大小ivars: 成員變量的列表methodLists: 方法列表cathe: 用于緩存最近使用的方法。
一個(gè)接收者對(duì)象接收到一個(gè)消息時(shí),它會(huì)根據(jù)isa指針去查找能夠響應(yīng)這個(gè)消息的對(duì)象。在實(shí)際使用中,這個(gè)對(duì)象只有一部分方法是常用的,很多方法其實(shí)很少用或者根本用不上。這種情況下,如果每次消息來時(shí),我們都是methodLists中遍歷一遍,性能勢(shì)必很差。這時(shí),cache就派上用場(chǎng)了。在我們每次調(diào)用過一個(gè)方法后,這個(gè)方法就會(huì)被緩存到cache列表中,下次調(diào)用的時(shí)候runtime就會(huì)優(yōu)先去cache中查找,如果cache沒有,才去methodLists中查找方法。這樣,對(duì)于那些經(jīng)常用到的方法的調(diào)用,提高了調(diào)用的效率。protocols: 協(xié)議列表
3、對(duì)象
objc_object是表示一個(gè)類的實(shí)例的結(jié)構(gòu)體
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
這個(gè)結(jié)構(gòu)體只有一個(gè)屬性,即指向其類的isa指針。這樣,當(dāng)我們向一個(gè)Objective-C對(duì)象發(fā)送消息時(shí),運(yùn)行時(shí)庫(kù)會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所屬的類。Runtime庫(kù)會(huì)在類的方法列表及父類的方法列表中去尋找與消息對(duì)應(yīng)的selector指向的方法,找到后即運(yùn)行這個(gè)方法。
4、元類(Meta Class)
meta-class是一個(gè)類對(duì)象的類
當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí),runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法;
而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta-class的方法列表中查找方法
meta-class之所以重要,是因?yàn)樗鎯?chǔ)著一個(gè)類的所有類方法。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class,因?yàn)槊總€(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)。

對(duì)于NSObject繼承體系來說,其實(shí)例方法對(duì)體系中的所有實(shí)例、類和meta-class都是有效的;而類方法對(duì)于體系內(nèi)的所有類和meta-class都是有效的。
5、objc_cache
cathe用于緩存調(diào)用過的方法。這個(gè)字段是一個(gè)指向objc_cache結(jié)構(gòu)體的指針,其定義如下:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method _Nullable buckets[1] OBJC2_UNAVAILABLE;
};
-
mask:一個(gè)整數(shù),指定分配的緩存bucket的總數(shù)。在方法查找過程中,runtime使用這個(gè)字段來確定開始線性查找數(shù)組的索引位置。指向方法selector的指針與該字段做一個(gè)AND位操作(index = (mask & selector))。這可以作為一個(gè)簡(jiǎn)單的hash散列算法。 -
occupied:一個(gè)整數(shù),指定實(shí)際占用的緩存bucket的總數(shù)。 -
buckets:指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個(gè)數(shù)組可能包含不超過mask+1個(gè)元素。需要注意的是,指針可能是NULL,表示這個(gè)緩存bucket沒有被占用,另外被占用的bucket可能是不連續(xù)的。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長(zhǎng)。
6、類與對(duì)象的操作函數(shù)
類的操作方法大部分是以class_為前綴的
對(duì)象的操作方法大部分是以objc_或object_為前綴
在runtime.h里面搜索Working with ,一共有8個(gè)搜索結(jié)果,搜索順序分別是:
Working with InstancesWorking with ClassesWorking with MethodsWorking with Instance VariablesWorking with PropertiesWorking with ProtocolsWorking with LibrariesWorking with Selectors
這里我們主要看Working with Classes、Working with Instances、 Working with Instance Variables里面的方法
6.1、動(dòng)態(tài)創(chuàng)建類
//創(chuàng)建一個(gè)新類和元類
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
size_t extraBytes) ;
// 在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類
void objc_registerClassPair(Class _Nonnull cls) ;
//銷毀一個(gè)類極其相關(guān)類
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
size_t extraBytes);
objc_allocateClassPair函數(shù):如果我們要?jiǎng)?chuàng)建一個(gè)根類,則superclass指定為Nil。extraBytes通常指定為0,該參數(shù)是分配給類和元類對(duì)象尾部的索引ivars的字節(jié)數(shù)。objc_disposeClassPair函數(shù)用于銷毀一個(gè)類,不過需要注意的是,如果程序運(yùn)行中還存在類或其子類的實(shí)例,則不能調(diào)用針對(duì)類調(diào)用該方法。
創(chuàng)建一個(gè)新類,先用objc_allocateClassPair,調(diào)用class_addMethod,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法、實(shí)例變量和屬性等。再調(diào)用objc_registerClassPair函數(shù)來注冊(cè)類,之后這個(gè)新類就可以在程序中使用了。
實(shí)例方法和實(shí)例變量應(yīng)該添加到類自身上,而類方法應(yīng)該添加到類的元類上
6.2、動(dòng)態(tài)創(chuàng)建對(duì)象
// 創(chuàng)建類實(shí)例
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置創(chuàng)建類實(shí)例
id objc_constructInstance ( Class cls, void *bytes );
// 銷毀類實(shí)例
void * objc_destructInstance ( id obj );
class_createInstance:創(chuàng)建實(shí)例時(shí),會(huì)在默認(rèn)的內(nèi)存區(qū)域?yàn)轭惙峙鋬?nèi)存。extraBytes額外的字節(jié)可以用來存儲(chǔ)類定義中定義之外的其他實(shí)例變量.objc_constructInstance:在指定的位置(bytes)創(chuàng)建類實(shí)例。objc_destructInstance:銷毀一個(gè)類的實(shí)例,但不會(huì)釋放并移除任何與其相關(guān)的引用。
類和對(duì)象的相關(guān)操作函數(shù)
// 返回指定對(duì)象的一份拷貝
id object_copy ( id obj, size_t size );
// 釋放指定對(duì)象占用的內(nèi)存
id object_dispose ( id obj );
// 修改類實(shí)例的實(shí)例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 獲取對(duì)象實(shí)例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向給定對(duì)象分配的任何額外字節(jié)的指針
void * object_getIndexedIvars ( id obj );
// 返回對(duì)象中實(shí)例變量的值
id object_getIvar ( id obj, Ivar ivar );
// 設(shè)置對(duì)象中實(shí)例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );
// 返回給定對(duì)象的類名
const char * object_getClassName ( id obj );
// 返回對(duì)象的類
Class object_getClass ( id obj );
// 設(shè)置對(duì)象的類
Class object_setClass ( id obj, Class cls );
// 獲取已注冊(cè)的類定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 創(chuàng)建并返回一個(gè)指向所有已注冊(cè)類的指針列表
Class * objc_copyClassList ( unsigned int *outCount );
// 返回指定類的類定義
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
// 返回指定類的元類
Class objc_getMetaClass ( const char *name );
6.3、父類(super_class)和元類(meta-class)
// 獲取類的父類
Class class_getSuperclass (Class _Nullable cls);
// 判斷給定的Class是否是一個(gè)元類
BOOL class_isMetaClass (Class _Nullable cls);
6.4、類名
// 獲取類的類名
const char * class_getName (Class _Nullable cls);
6.5、版本號(hào)
// 獲取版本號(hào)
int class_getVersion (Class _Nullable cls) ;
// 設(shè)置版本號(hào)
void class_setVersion (Class _Nullable cls, int version);
6.6、實(shí)例大小
// 獲取實(shí)例大小
size_t class_getInstanceSize (Class _Nullable cls);
6.7 成員變量
// 獲取類中指定名稱實(shí)例成員變量的信息
Ivar class_getInstanceVariable (Class _Nullable cls, const char * _Nonnull name);
// 獲取類成員變量的信息
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name);
// 添加成員變量
BOOL class_addIvar (Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types) ;
// 獲取整個(gè)成員變量列表
Ivar * class_copyIvarList (Class _Nullable cls, unsigned int * _Nullable outCount) ;
-
class_getInstanceVariable: 根據(jù)name返回指定的對(duì)象成員變量信息(objc_ivar結(jié)構(gòu)體)
class_getClassVariable: 根據(jù)name返回指定的類成員變量信息(objc_ivar結(jié)構(gòu)體),一般認(rèn)為Objective-C不支持類變量class_addIvar: 參數(shù)分別是,成員變量所屬類、成員變量名、對(duì)齊方式、成員變量類型
Objective-C不支持往已存在的類中添加實(shí)例成員變量,因此不管是系統(tǒng)庫(kù)提供的提供的類,還是我們自定義的類,都無法動(dòng)態(tài)添加成員變量,但是動(dòng)態(tài)創(chuàng)建類的可以添加成員變量,只能在objc_allocateClassPair與objc_registerClassPair之間
如添加一個(gè)NSString類型的ivar1變量
class_addIvar(cls, "ivar1", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
-
class_copyIvarList: 它返回一個(gè)指向成員變量信息的數(shù)組,數(shù)組中每個(gè)元素是指向該成員變量信息的objc_ivar結(jié)構(gòu)體的指針,outCount指針返回?cái)?shù)組的大小
注意 : 返回的列表不包含父類的成員變量和屬性,必須使用free()來釋放這個(gè)數(shù)組
6.8 屬性
// 獲取指定的屬性
objc_property_t class_getProperty(Class _Nullable cls, const char * _Nonnull name);
// 獲取屬性列表
objc_property_t class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount);
// 為類添加屬性
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount);
// 替換類的屬性
void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
-
class_getProperty: 根據(jù)name返回指定的類屬性(objc_property結(jié)構(gòu)體) -
class_copyPropertyList: 它返回一個(gè)指向?qū)傩缘臄?shù)組,數(shù)組中每個(gè)元素是指向該屬性的objc_property結(jié)構(gòu)體的指針,outCount指針返回?cái)?shù)組的大小
注意 : 返回的列表不包含父類的屬性,必須使用free()來釋放這個(gè)數(shù)組
-
class_addProperty: 參數(shù)含義分別是:類名、屬性名、屬性特性、屬性特性個(gè)數(shù)
如添加屬性名為@property (nonatomic , copy) NSString *property1;的屬性
objc_property_attribute_t type = { "T", "@\"NSString\"" };
objc_property_attribute_t ownership = { "C", "" }; // C = copy
objc_property_attribute_t nonatomic = { "N", "" }; //nonatomic
objc_property_attribute_t backingivar = { "V", "_property1" };//V 實(shí)例變量
objc_property_attribute_t attrs[] = { type, ownership,nonatomic, backingivar };
class_addProperty(cls, "property1", attrs, 4);
-
class_replaceProperty: 替換類的屬性
6.9、方法
//添加方法
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types);
//獲取實(shí)例方法
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name);
//獲取類方法
Method class_getClassMethod(Class _Nullable cls, SEL _Nonnull name);
//獲取所有方法數(shù)組
Method class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount);
//替代方法的實(shí)現(xiàn)
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types) ;
//返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) ;
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name) ;
//對(duì)象是否響應(yīng)指定的selector
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) ;
1.class_addMethod : 參數(shù)含義分別是:添加方法的類、添加方法的編號(hào)、方法實(shí)現(xiàn)、方法類型,使用這個(gè)方法添加方法會(huì)覆蓋父類的方法實(shí)現(xiàn),但不會(huì)取代本類中已存在的實(shí)現(xiàn),如果本類中包含一個(gè)同名的實(shí)現(xiàn),則函數(shù)會(huì)返回NO。如果要修改已存在實(shí)現(xiàn),可以使用method_setImplementation
如添加一個(gè)eat方法
class_addMethod([self class],@selector(eat:),(IMP) eatIMP, "v@:@");
-
class_getInstanceMethod: 根據(jù)name返回指定的對(duì)象方法(objc_method結(jié)構(gòu)體) -
class_getClassMethod: 根據(jù)name返回指定的類方法(objc_method結(jié)構(gòu)體)
注意 : 這兩個(gè)函數(shù)都會(huì)去搜索父類的實(shí)現(xiàn)
-
class_copyMethodList: 返回一個(gè)指向?qū)嵗椒ǖ臄?shù)組,數(shù)組中每個(gè)元素都是該方法信息的objc_method結(jié)構(gòu)體指針,outCount返回?cái)?shù)組的大小。
如果需要獲取類方法,則可以使用class_copyMethodList(object_getClass(cls), &count),因?yàn)橐粋€(gè)類的實(shí)例方法是定義在元類里面
注意 : 返回的列表不包含父類的方法,必須使用free()來釋放這個(gè)數(shù)組
class_replaceMethod: 如果類中不存在name指定的方法,則類似于class_addMethod函數(shù)一樣會(huì)添加方法;如果類中已存在name指定的方法,則類似于method_setImplementation一樣替代原方法的實(shí)現(xiàn)。class_getMethodImplementation: 返回一個(gè)指向方法實(shí)現(xiàn)函數(shù)的指針,在向類實(shí)例發(fā)送消息時(shí)會(huì)被調(diào)用,這個(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: 我們通常使用NSObject類的respondsToSelector:或instancesRespondToSelector:方法來達(dá)到相同目的。
6.10、協(xié)議
// 添加協(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: 可以使用NSObject類的conformsToProtocol:方法來替代。class_copyProtocolList: 返回一個(gè)指向協(xié)議的數(shù)組,數(shù)組中每個(gè)元素都是該協(xié)議信息的objc_object結(jié)構(gòu)體指針,outCount返回?cái)?shù)組的大小。
注意 : 返回的列表不包含父類的協(xié)議,必須使用free()來釋放這個(gè)數(shù)組