通過runtime進(jìn)行數(shù)據(jù)分析

(一)運(yùn)行時(shí)(runtime)

  • 運(yùn)行時(shí)(runtime)是一種面向?qū)ο蟮木幊陶Z言的運(yùn)行環(huán)境.
  • 運(yùn)行時(shí)(runtime)Objective-C的核心, Objective-C就是基于運(yùn)行時(shí)(runtime)的.
  • Objective-C是基于C語言加入了面向?qū)ο筇匦院拖⑥D(zhuǎn)發(fā)機(jī)制的動(dòng)態(tài)語言.
  • Objective-C需要一個(gè)編譯器,還需要Runtime系統(tǒng)來動(dòng)態(tài)創(chuàng)建類和對象,進(jìn)行消息發(fā)送和轉(zhuǎn)發(fā)。
    • Objective-C最主要的特點(diǎn)就是在程序運(yùn)行時(shí), 以發(fā)送消息的方式調(diào)用方法.
  • C語言的函數(shù)調(diào)用方式是使用靜態(tài)綁定(static binding).在編譯期就能決定運(yùn)行時(shí)所應(yīng)調(diào)用的函數(shù).
  • Objective-C中,如果向某對象傳遞消息,那就會(huì)使用動(dòng)態(tài)綁定機(jī)制來決定需要調(diào)用的方法。在底層,所有方法都是普通的C語言函數(shù)。
  • 頭文件 #import <objc/runtime.h> #import <objc/message.h> ...

1.OC方法調(diào)用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self testDemo:nil];
}

- (void)testDemo:(id)param {
    NSLog(@"%s",__func__);
}

2.分析 [self testDemo];

  1. self 叫做 接收者(receiver)
  2. testDemo 叫做 選擇子(selector)
  3. 選擇子參數(shù)合起來稱為 消息(message)
  4. 編譯器看到此消息后,將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用
  5. 所調(diào)用的函數(shù)是消息傳遞機(jī)制中的核心函數(shù),叫做objc_msgSend()

3.運(yùn)行時(shí)(runtime)消息發(fā)送 == OC方法調(diào)用底層實(shí)現(xiàn)

  • 運(yùn)行時(shí)(runtime)消息發(fā)送函數(shù)
    • 提示 : OBJC2_UNAVAILABLE是一個(gè)Apple對Objc系統(tǒng)運(yùn)行版本進(jìn)行約束的宏定義,主要為了兼容非Objective-C 2.0的遺留版本
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 消息發(fā)送 : iOS8以后的特殊寫法
    ((void(*)(id,SEL,id))objc_msgSend)(self,@selector(testDemo:),nil);
}

- (void)testDemo:(id)param {
    NSLog(@"%s",__func__);
}

(二)數(shù)據(jù)類型分析

1.SEL : 方法選擇器(指向objc_selector結(jié)構(gòu)體的指針)

typedef struct objc_selector *SEL;
  • 可以通過Objc編譯器命令@selector()或者Runtime系統(tǒng)的sel_registerName()函數(shù)來獲取一個(gè)SEL類型的方法選擇器.
  • 如果知道selector對應(yīng)的方法名是什么,可以通過NSString* NSStringFromSelector(SEL aSelector)方法將SEL轉(zhuǎn)化為OC字符串.

2.id : 對象(指向objc_object結(jié)構(gòu)體的指針)

// objc_object結(jié)構(gòu)體
struct objc_object {
    // id的成員 : isa
    Class isa  OBJC_ISA_AVAILABILITY;
};
// 指向objc_object結(jié)構(gòu)體指針
typedef struct objc_object *id;
  • 包含一個(gè)Class isa成員.
  • 根據(jù)isa指針就可以找到對象所屬的類.

3.Class : 對象所屬的類(指向objc_class結(jié)構(gòu)體的指針)

typedef struct objc_class *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;
    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;

4.Method : 方法(指向objc_method結(jié)構(gòu)體的指針)

typedef struct objc_method *Method;
struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}
  • objc_method存儲(chǔ)了方法名(method_name)、方法類型(method_types)方法實(shí)現(xiàn)(method_imp)等信息.
  • method_imp的數(shù)據(jù)類型是IMP,它是一個(gè)函數(shù)指針.

5.IMP : 方法實(shí)現(xiàn)(指向方法實(shí)現(xiàn)的指針)

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
  • IMP本質(zhì)上就是一個(gè)函數(shù)指針,指向方法的實(shí)現(xiàn).
  • 當(dāng)向某個(gè)對象發(fā)送一條信息時(shí),可以由這個(gè)函數(shù)指針來指定方法的實(shí)現(xiàn),它最終就會(huì)執(zhí)行那段代碼.

6.Ivar : 實(shí)例變量(指向objc_ivar結(jié)構(gòu)體的指針)

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
}

7.Cache : 緩存(指向結(jié)構(gòu)體objc_cache的指針)

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;
}
  • 其實(shí)就是一個(gè)存儲(chǔ)Method的鏈表,主要是為了優(yōu)化方法調(diào)用的性能.

8.類關(guān)系圖

  • 實(shí)例對象在運(yùn)行時(shí)被表示成 objc_object 類型結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)部有個(gè)isa指針指向 objc_class 結(jié)構(gòu)體。
  • objc_class 內(nèi)部保存了類的變量和方法列表以及其他一些信息,并且還有一個(gè)isa指針。這個(gè)isa指針會(huì)指向 metaClass(元類),元類里保存了這個(gè)類的類方法列表。
  • 元類里也有一個(gè)isa指針,這個(gè)isa指針,指向的是根元類,根元類的isa指針指向自己

(三)objc_class中信息查看

1.Class : 對象所屬的類(指向objc_class結(jié)構(gòu)體的指針)

typedef struct objc_class *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;
    long info                                                OBJC2_UNAVAILABLE;  // 位標(biāo)識(shí)
    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;

參考官方地址:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1

類型編碼參考地址:http://www.itdecent.cn/p/f4129b5194c0

2.代碼演練

(1)獲取類名
/**
 獲取類名

 @param cls 要獲取類名的類
 @return 類名
 */
+ (NSString *)getClassName:(Class)cls {
    // 獲取類名(C語言類型)
    const char *cName = class_getName(cls);
    // OC類型類名
    NSString *className = [NSString stringWithUTF8String:cName];

    return className;
}
(2)獲取成員變量列表
/**
 獲取成員變量列表(帶下劃線的都獲取)

 @param cls 要獲取成員變量列表的類
 @return 成員變量數(shù)組(成員變量名字和類型組合的字典數(shù)組)
 */
+ (NSArray *)getIvarList:(Class)cls {
    // 成員變量個(gè)數(shù)
    unsigned int count;
    // 獲取所有的成員變量
    Ivar *ivarList = class_copyIvarList(cls, &count);

    // 準(zhǔn)備數(shù)組容器
    NSMutableArray *ivarArrM = [NSMutableArray arrayWithCapacity:count];

    // 遍歷成員變量
    for (NSInteger i = 0; i < count; i++) {
        // 獲取成員變量名字
        const char *ivarName = ivar_getName(ivarList[i]);
        // 獲取成員變量類型
        const char *ivarType = ivar_getTypeEncoding(ivarList[i]);

        // 成員變量名字和類型組合的字典容器
        NSMutableDictionary *ivarDictM = [NSMutableDictionary dictionary];
        ivarDictM[@"name"] = [NSString stringWithUTF8String:ivarName];
        ivarDictM[@"type"] = [NSString stringWithUTF8String:ivarType];

        // 添加到數(shù)組容器
        [ivarArrM addObject:ivarDictM];
    }
    free(ivarList);
    return ivarArrM.copy;
}
(3)獲取屬性列表
/**
 獲取屬性列表(有setter和getter方法的屬性)

 @param cls 要獲取屬性列表的類
 @return 屬性數(shù)組(屬性名字和屬性描述的字典數(shù)組)
 */
+ (NSArray *)getPropertyList:(Class)cls {
    // 成員屬性個(gè)數(shù)
    unsigned int count;
    // 獲取所有成員變量
    objc_property_t *propertyList = class_copyPropertyList(cls, &count);

    // 成員屬性容器
    NSMutableArray *propertyArrM = [NSMutableArray arrayWithCapacity:count];

    // 遍歷成員屬性
    for (NSInteger i = 0; i < count; i++) {
        // 獲取屬性名字和屬性的屬性描述
        NSString *name = [NSString stringWithUTF8String:property_getName(property)];
        NSString *attr = [NSString stringWithUTF8String:property_getAttributes(property)];
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[@"name"] = name;
        dict[@"attr"] = attr;
        // 添加到數(shù)組容器
        [propertyArrM addObject:[dict copy]];
    }
    free(propertyList);
    return propertyArrM.copy;
}
(4)類屬性中的屬性示例
@property (nonatomic,strong, setter=setPublicProperty:) NSArray *publicProperty01;
  • property_getAttributes輸出為:
attr = "T@\"NSArray\",&,N,SsetPublicProperty:,V_publicProperty01";
name = publicProperty01;

其中 attr 的解釋為:

  • T 代表類型標(biāo)識(shí)
  • @ 代表為對象類型
  • NSArray 表示其實(shí)際類型
  • & 代表 retain 強(qiáng)引用(copy 用 C, weak 用 W)
  • N 代表 nonatomic (代表 natomic)
  • SsetPublicProperty: 前面大寫S代表指定了 setter,后面跟著代表具體方法
  • V_publicProperty01 V代表其對應(yīng)的成員,后面為成員的名字
(5)獲取方法列表
/**
 獲取類的實(shí)例方法 : 屬性的setter和getter方法,對象方法,不包括類方法

 @param cls 要獲取類的實(shí)例方法的類
 @return 方法數(shù)組
 */
+ (NSArray *)getMethodList:(Class)cls {
    // 實(shí)例方法個(gè)數(shù)
    unsigned int count;
    // 獲取所有方法(不包括類方法)
    Method *methodList = class_copyMethodList(cls, &count);

    // 方法容器
    NSMutableArray *methodArrM = [NSMutableArray arrayWithCapacity:count];

    // 遍歷所有方法
    for (NSInteger i = 0; i < count; i++) {
        // 獲取數(shù)據(jù)
        NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
        NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[@"name"] = name;
        dict[@"type"] = type;
        // 添加到數(shù)組
        [methodArrM addObject:[dict copy]];
    }

    // 通過元類獲取類方法
    Class metaCls = objc_getMetaClass(class_getName(cls));
    methods = class_copyMethodList(metaCls, &count);
    for (NSInteger i = 0; i < count; i++) {
        Method method = methods[i];
        // 獲取數(shù)據(jù)
        NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
        NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[@"name"] = name;
        dict[@"type"] = type;
        // 添加到數(shù)組
        [arrayM addObject:[dict copy]];
    }

    free(methodList);
    return methodArrM.copy;
}
(6)方法類型編碼示例
/// 公有方法2
- (void)publicMethodd02:(NSString *)str append:(int)age;
  • method_getTypeEncoding 的輸出為:
name = "publicMethodd02:append:";
type = "v28@0:8@16i24";

其中type解釋為:

  • v 代表返回值為void
  • 28 代表整個(gè)方法參數(shù)占位的總長度
  • @0 @代表對象,objc_msgSend 函數(shù)傳入的第1個(gè)參數(shù)(self), 后面的0代表位置0開始
  • :8 :代表SEL,objc_msgSend 函數(shù)傳入的第2個(gè)參數(shù)(self),后面的8代表位置8開始
  • @16 @代表第1個(gè)參數(shù)的類型為對象類型,后面的16代表位置8開始
  • i24 i代表第2個(gè)參數(shù)的類型為int類型,后面的24代表位置24開始
(7)獲取協(xié)議列表
/**
 獲取類的協(xié)議列表

 @param cls 獲取協(xié)議列表的類
 @return 協(xié)議數(shù)組
 */
+ (NSArray *)getProtocolList:(Class)cls {

    // 協(xié)議個(gè)數(shù)
    unsigned int count;
    // 獲取協(xié)議列表
    Protocol * __unsafe_unretained *protocolList = class_copyProtocolList(cls, &count);

    // 協(xié)議容器
    NSMutableArray *protocolArrM = [NSMutableArray arrayWithCapacity:count];

    // 遍歷協(xié)議列表
    for (NSInteger i = 0; i < count; i++) {
        // 獲取協(xié)議名字
        const char *protocolName = protocol_getName(protocolList[i]);
        // 添加到協(xié)議容器
        [protocolArrM addObject:[NSString stringWithUTF8String:protocolName]];
    }
    free(protocolList);
    return protocolArrM.copy;
}

(四)問答

問答1 : 為什么id可以指向任何對象?

  • 類是用 objc_class 結(jié)構(gòu)體表示的,對象是用objc_object 結(jié)構(gòu)體表示的,objc_class繼承自objc_object,而id就是objc_object類型的,
struct objc_class : objc_object {
  // Class ISA;
  Class superclass;
  cache_t cache;             // formerly cache pointer and vtable
  class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

問答2 : 分類中是否可以定義屬性?

  • 可以定義屬性,但是系統(tǒng)不會(huì)實(shí)現(xiàn)setter和getter
  • 定義的屬性無法存值,因?yàn)闆]有成員變量鏈表struct objc_ivar_list *ivars
  • 但是,可以使用運(yùn)行時(shí)的關(guān)聯(lián)給分類添加屬性
typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
} category_t;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,072評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,892評(píng)論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,249評(píng)論 0 9
  • objc_getAssociatedObject返回與給定鍵的特定對象關(guān)聯(lián)的值。ID objc_getAssoci...
    有一種再見叫青春閱讀 1,762評(píng)論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 839評(píng)論 0 2

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