關(guān)于runtime的一些數(shù)據(jù)類型的分析

總述

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

 import <objc/runtime.h>
 import <objc/message.h>

OC方法調(diào)用

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

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

分析[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()

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

運行時(runtime)消息發(fā)送函數(shù):OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC2_UNAVAILABLE是一個Apple對Objc系統(tǒng)運行版本進(jìn)行約束的宏定義账蓉,主要為了兼容非Objective-C 2.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ù)類型分析

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

typedef struct objc_selector *SEL;

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

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;

1.包含一個Class isa成員.
2.根據(jù)isa指針就可以找到對象所屬的類.

Class : 對象所屬的類(指向objc_class結(jié)構(gòu)體的指針)<if見面自動加井號>

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;

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存儲了方法名(method_name)逾一、方法類型(method_types)和方法實現(xiàn)(method_imp)等信息.
method_imp的數(shù)據(jù)類型是IMP铸本,它是一個函數(shù)指針.

IMP : 方法實現(xiàn)(指向方法實現(xiàn)的指針)

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif

IMP本質(zhì)上就是一個函數(shù)指針,指向方法的實現(xiàn).
當(dāng)向某個對象發(fā)送一條信息時遵堵,可以由這個函數(shù)指針來指定方法的實現(xiàn)箱玷,它最終就會執(zhí)行那段代碼.

Ivar : 實例變量(指向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
}

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;
}

其實就是一個存儲Method的鏈表,主要是為了優(yōu)化方法調(diào)用的性能.

image.png

類關(guān)系圖

image.png

1.實例對象在運行時被表示成 objc_object 類型結(jié)構(gòu)體陌宿,結(jié)構(gòu)體內(nèi)部有個isa指針指向 objc_class 結(jié)構(gòu)體锡足。
2.objc_class 內(nèi)部保存了類的變量和方法列表以及其他一些信息,并且還有一個isa指針壳坪。這個isa指針會指向 metaClass(元類)舶得,元類里保存了這個類的類方法列表。
3.元類里也有一個isa指針爽蝴,這個isa指針沐批,指向的是根元類,根元類的isa指針指向自己

objc_class中信息查看

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)識
    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;

代碼示例

獲取類名
/**
 獲取類名

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

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

 @param cls 要獲取成員變量列表的類
 @return 成員變量數(shù)組(成員變量名字和類型組合的字典數(shù)組)
 */
+ (NSArray *)getIvarList:(Class)cls {
    // 成員變量個數(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;
}
獲取屬性列表
/**
 獲取屬性列表(有setter和getter方法的屬性)

 @param cls 要獲取屬性列表的類
 @return 屬性數(shù)組(屬性名字和屬性描述的字典數(shù)組)
 */
+ (NSArray *)getPropertyList:(Class)cls {
    // 成員屬性個數(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;
}

類屬性中的屬性示例

@property (nonatomic,strong, setter=setPublicProperty:) NSArray *publicProperty01;
property_getAttributes輸出為:
attr = "T@\"NSArray\",&,N,SsetPublicProperty:,V_publicProperty01"; name = publicProperty01;
其中 attr 的解釋為:

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

 @param cls 要獲取類的實例方法的類
 @return 方法數(shù)組
 */
+ (NSArray *)getMethodList:(Class)cls {
    // 實例方法個數(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;
}
方法類型編碼示例
/// 公有方法2
- (void)publicMethodd02:(NSString *)str append:(int)age;

method_getTypeEncoding 的輸出為:
name = "publicMethodd02:append:"; type = "v28@0:8@16i24";
其中type解釋為:

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

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

    // 協(xié)議個數(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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膘怕,一起剝皮案震驚了整個濱河市癣疟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌忙迁,老刑警劉巖设预,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徙歼,死亡現(xiàn)場離奇詭異,居然都是意外死亡鳖枕,警方通過查閱死者的電腦和手機(jī)魄梯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宾符,“玉大人酿秸,你說我怎么就攤上這事∥禾蹋” “怎么了辣苏?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵肝箱,是天一觀的道長。 經(jīng)常有香客問我稀蟋,道長煌张,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任退客,我火速辦了婚禮骏融,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萌狂。我一直安慰自己档玻,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布茫藏。 她就那樣靜靜地躺著窃肠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刷允。 梳的紋絲不亂的頭發(fā)上冤留,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音树灶,去河邊找鬼纤怒。 笑死,一個胖子當(dāng)著我的面吹牛天通,可吹牛的內(nèi)容都是我干的泊窘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼像寒,長吁一口氣:“原來是場噩夢啊……” “哼烘豹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诺祸,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤携悯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筷笨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憔鬼,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年胃夏,在試婚紗的時候發(fā)現(xiàn)自己被綠了轴或。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡仰禀,死狀恐怖照雁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情答恶,我是刑警寧澤饺蚊,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布萍诱,位于F島的核電站,受9級特大地震影響卸勺,放射性物質(zhì)發(fā)生泄漏砂沛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一曙求、第九天 我趴在偏房一處隱蔽的房頂上張望碍庵。 院中可真熱鬧,春花似錦悟狱、人聲如沸静浴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苹享。三九已至,卻和暖如春浴麻,著一層夾襖步出監(jiān)牢的瞬間得问,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工软免, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留宫纬,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓膏萧,卻偏偏與公主長得像漓骚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子榛泛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 我們常常會聽說 Objective-C 是一門動態(tài)語言蝌蹂,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,172評論 0 7
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 748評論 0 1
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識曹锨,它使得 Objective-C 如虎添翼孤个,具備了靈活的...
    lylaut閱讀 792評論 0 4
  • objc_getAssociatedObject返回與給定鍵的特定對象關(guān)聯(lián)的值。ID objc_getAssoci...
    有一種再見叫青春閱讀 1,564評論 0 7
  • 文中的實驗代碼我放在了這個項目中艘希。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 913評論 0 6