以前只是粗略的知道runtime的大致幾種用法, 沒有系統(tǒng)的學(xué)習(xí)并操作過, 最近更新簡書, 好多14年的疑問與CTO的指導(dǎo)都豁然開朗.
** 面向?qū)ο蟮幕靖拍? 每個對象都會有一個它所屬的類。這對所有數(shù)據(jù)結(jié)構(gòu)有效。任何數(shù)據(jù)結(jié)構(gòu),只要在恰當(dāng)?shù)奈恢镁哂幸粋€指針指向一個class,那么,它都可以被認為是一個對象。**
以下代碼演示運行時創(chuàng)建一個NSError的子類,同時添加一個實例方法給它:
- (void)viewDidLoad {
[super viewDidLoad];
//運行時創(chuàng)建NSError的子類, 使用objc_allocateClassPair開辟空間
Class newClass = objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
/* 動態(tài)添加方法:
第一個參數(shù)表示Class cls 類型;
第二個參數(shù)表示待調(diào)用的方法名稱;
第三個參數(shù)(IMP)myAddingFunction,IMP一個函數(shù)指針,這里表示指定具體實現(xiàn)方法ReportFunction;
第四個參數(shù)表方法的參數(shù),0代表沒有參數(shù);
*/
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
//使用objc_registerClassPair注冊你創(chuàng)建的這個類
objc_registerClassPair(newClass);
//初始化剛創(chuàng)建的newClass的實例對象
id instanceOfNewClass = [[newClass alloc]initWithDomain:@"some Domain" code:0 userInfo:nil];
//利用實例方法調(diào)用剛才添加的方法
[instanceOfNewClass performSelector:@selector(report)];
}
//具體的實現(xiàn)(方法的內(nèi)部都默認包含兩個參數(shù)Class類和SEL方法,被稱為隱式參數(shù)。)
void ReportFunction(id self, SEL _cmd)
{
NSLog(@"This object is %p.",self);
NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]);
Class currentClass = [self class];
for( int i = 1; i < 5; ++i )
{
NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]);
NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class]));
}
在OC中,一個
對象所屬于哪個類,是由它的isa指針指向的。這個isa指針指向這個對象所屬的class。
實際上,OC中對象的定義是如下的樣子:
typedef struct objc_object {
Class isa;
}*id;
定義表明:任何以一個指向Class的指針作為首個成員的數(shù)據(jù)結(jié)構(gòu)都可以被認為是一個objc_object.
由此的特性為: 你可以向OC中的任何對象發(fā)送消息
第一個概念: meta-class
1.OC的類其實也是一個對象,意思就是你可以向一個類發(fā)送消息。一個類不管包含哪種方法,他們都是以一個isa作為第一個字段,接著是superclass字段。如下
typedef struct objc_class *Class;
struct objc_class{
Class isa;
Class super_class;
/*followed by runtime specific details...*/
};
為了可以調(diào)用類方法,這個類的isa指針必須指向一個包含這些類方法的類結(jié)構(gòu)體。
這樣就引出了meta-class的概念:meta-class是一個類對象的類。
1.當(dāng)你向一個**對象**發(fā)送消息時,runtime會在這個對象所屬的那個類的方法列表中查找。
2.當(dāng)你向一個**類**發(fā)送消息時,runtime會在這個類的meta-class的方法列表中查找。
meta-class之所以重要,是因為它存儲著一個類的所有類方法。每個類都會有一個單獨的meta-class,因為每個類的類方法基本不可能完全相同。
meta-class類的
任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己所屬的類。所有的meta-class使用基類的meta-class作為它們的父類,而基類的meta-class也是屬于它自己,也就是說基類的meta-class的isa指針指向它自己。
Greg Parker給出了一份精彩的圖譜來展示這些關(guān)系:

結(jié)論:
1. 每個類都有單獨的meta-class;
2. 類似于所有的類都繼承自基類NSObject, 每個類的meta-class同樣繼承自基類meta-class;
3. 類與meta-class是一一對應(yīng)的關(guān)系.
第二個概念: 類與對象在runtime中的數(shù)據(jù)結(jié)構(gòu)
1. objc/runtime.h中objc_class結(jié)構(gòu)體的定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
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; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
說了兩個概念, 似乎依然不所云, 下面就舉例子來解釋你日常寫的代碼背后到底經(jīng)過了哪些步驟.
實例:
NSArray *array = [[NSArray alloc] init];
流程為:
1. [NSArray alloc]先被執(zhí)行。因為NSArray沒有+alloc方法,于是去父類NSObject對應(yīng)的meta-class中去查找;
2. 父類NSObject響應(yīng)后開始分配內(nèi)存,然后將isa指針指向NSArray類, 同時將alloc方法加進cache列表里面<等于運行時添加方法>;
3. 然后執(zhí)行-init方法, 如果相應(yīng)直接添加到NSArray對應(yīng)的meta-class中, 否則去父類查找;
4. 后期操作將直接從NSArray中的cache中調(diào)用.
第三步驟: 認識Runtime中類與對象的操作函數(shù)
runtime提供了大量的函數(shù)來操作類和對象, 類的操作方法大部分是以class為前綴的,而對象的操作方法大部分是以objc或object_為前綴。
3.1.操作類的函數(shù)
類相關(guān)的判斷
//引入頭文件
#import <objc/runtime.h>
// 獲取類的類名
const char * class_getName ( Class cls );
// 獲取類的父類
Class class_getSuperclass ( Class cls );
// 判斷給定的Class是否是一個元類
BOOL class_isMetaClass ( Class cls );
// 獲取實例變量的大小
size_t class_getInstanceSize ( Class cls );
成員變量祥相關(guān)
// 獲取類中指定名稱實例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 獲取類成員變量的信息<不包含父類的成員變量和屬性>
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成員變量<這個方法只能在objc_allocateClassPair函數(shù)與objc_registerClassPair之間調(diào)用>
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
// 獲取整個成員變量列表<使用后必須使用free()來釋放這個數(shù)組。>
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
屬性相關(guān)
// 獲取指定的屬性
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 );
方法相關(guān)
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 獲取實例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實現(xiàn)<如果有同名函數(shù)則替換, 否則會class_addMethod>
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類實例是否響應(yīng)指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
注: IMP是一個函數(shù)指針.實現(xiàn)函數(shù)(IMP參數(shù)指向的函數(shù))至少需要兩個參數(shù)
如下
void myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
協(xié)議(objc_protocol_list)相關(guān)
// 添加協(xié)議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回類是否實現(xiàn)指定的協(xié)議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回類實現(xiàn)的協(xié)議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
動態(tài)創(chuàng)建類和操作對象
動態(tài)創(chuàng)建類
// 創(chuàng)建一個新類和元類; superclass為nil, extraBytes指定為0, 該參數(shù)是分配給類和元類對象尾部的索引ivars的字節(jié)數(shù)
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes )
// 銷毀一個類及其相關(guān)聯(lián)的類
void objc_disposeClassPair ( Class cls );
// 在應(yīng)用中注冊由objc_allocateClassPair創(chuàng)建的類
void objc_registerClassPair ( Class cls );
對類的補充說明
1. 為了創(chuàng)建一個新類,我們需要調(diào)用objc_allocateClassPair。然后使用諸如class_addMethod,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法、實例變量和屬性等。完成這些后,我們需要調(diào)objc_registerClassPair函數(shù)來注冊類,之后這個新類就可以在程序中使用了;
2. objc_disposeClassPair函數(shù)用于銷毀一個類,不過需要注意的是,如果程序運行中還存在類或其子類的實例,則不能調(diào)用針對類調(diào)用該方法。
動態(tài)創(chuàng)建對象
// 創(chuàng)建類實例, 調(diào)用class_createInstance的效果與+alloc方法類似。不過在使用class_createInstance時,我們需要確切的知道我們要用它來做什么。ARC無法使用
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置創(chuàng)建類實例
id objc_constructInstance ( Class cls, void *bytes );
// 銷毀類實例<銷毀一個類的實例,但不會釋放并移除任何與其相關(guān)的引用。>
void * objc_destructInstance ( id obj );
操作函數(shù)
1.針對整個對象進行操作的函數(shù),這類函數(shù)包含
// 返回指定對象的一份拷貝
id object_copy ( id obj, size_t size );
// 釋放指定對象占用的內(nèi)存
id object_dispose ( id obj );
2.針對對象實例變量進行操作的函數(shù),這類函數(shù)包含:
// 修改類實例的實例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 獲取對象實例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向給定對象分配的任何額外字節(jié)的指針
void * object_getIndexedIvars ( id obj );
// 返回對象中實例變量的值
id object_getIvar ( id obj, Ivar ivar );
// 設(shè)置對象中實例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );
如果實例變量的Ivar已經(jīng)知道,那么調(diào)用object_getIvar會比object_getInstanceVariable函數(shù)快,相同情況下,object_setIvar也比object_setInstanceVariable快
3.針對對象的類進行操作的函數(shù),這類函數(shù)包含:
// 返回給定對象的類名
const char * object_getClassName ( id obj );
// 返回對象的類
Class object_getClass ( id obj );
// 設(shè)置對象的類
Class object_setClass ( id obj, Class cls );
獲取類定義
// 獲取已注冊的類定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 創(chuàng)建并返回一個指向所有已注冊類的指針列表
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 );
參考:
南峰子的技術(shù)博客
What is a meta-class in Objective-C?
詳解Objective-C的meta-class
Runtime的幾個小例子
更多精彩內(nèi)容請關(guān)注“IT實戰(zhàn)聯(lián)盟”哦~~~
