iOS Runtime學(xué)習(xí)與使用

以前只是粗略的知道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都使用NSObjectmeta-class作為自己所屬的類。所有的meta-class使用基類的meta-class作為它們的父類,而基類的meta-class也是屬于它自己,也就是說基類的meta-class的isa指針指向它自己。

Greg Parker給出了一份精彩的圖譜來展示這些關(guān)系:

Paste_Image.png

結(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 );

詳見github的Demo


參考:
南峰子的技術(shù)博客
What is a meta-class in Objective-C?
詳解Objective-C的meta-class
Runtime的幾個小例子

更多精彩內(nèi)容請關(guān)注“IT實戰(zhàn)聯(lián)盟”哦~~~


IT實戰(zhàn)聯(lián)盟.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,068評論 0 9
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了...
    _燴面_閱讀 1,423評論 1 5
  • Objective-C語言是一門動態(tài)語言,他將很多靜態(tài)語言在編譯和鏈接時期做的事情放到了運行時來處理。這種動態(tài)語言...
    tigger丨閱讀 1,599評論 0 8
  • 本文轉(zhuǎn)載自:http://southpeak.github.io/2014/10/25/objective-c-r...
    idiot_lin閱讀 1,031評論 0 4
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,892評論 33 466

友情鏈接更多精彩內(nèi)容