Runtime-內存模型

Runtime 是 iOS編程人員的核心基礎知識峰鄙,Objc Runtime使得C具有了面向對象能力,在程序運行時創(chuàng)建太雨,檢查吟榴,修改類、對象和它們的方法囊扳。
runtime是開源的吩翻,GitHub 有可調式的源碼 objc4_debug(待工程配置過程??),也可以去官網(wǎng)找 objc4.
學習參考 蘋果官方的Runtime編程指南

isa & id

第一印象指針锥咸,在<objc.h>中可以看到:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
};
  1. isaobjc_object一個屬性狭瞎,類型是Class
  2. Class 是指向 objc_class的一個指針,存放著objc_class的地址
  3. id是一個objc_object結構類型的指針搏予,這個類型的對象能夠轉換成任何一種對象

Class

objc_class 是什么呢熊锭?讓我們看到<objc/runtime.h>

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE; // 父類 
    const char * _Nonnull name                               OBJC2_UNAVAILABLE; // 類名
    long version                                             OBJC2_UNAVAILABLE; //  類的版本信息,默認為0
    long info                                                OBJC2_UNAVAILABLE; // 類信息雪侥,供運行期間使用的一些位標識
    long instance_size                                       OBJC2_UNAVAILABLE; // 該類的實例變量大小
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE; // 方法定義的鏈表
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE; // 方法緩存
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
  1. objc_class中也有一個isa指針碗殷,指向Meta Class。作用:因為 Objc 的類本身也是一個對象速缨,為了處理這個關系锌妻,runtime就創(chuàng)造了Meta Class,當給類發(fā)送 [NSObject alloc] 的消息時旬牲,實際上是把這個消息發(fā)給 NSObject Class Object仿粹。
  2. cache 作用:對象接受到一個消息搁吓,首次會根據(jù)isa指針查找消息對象,然后在methodLists遍歷吭历,調用后加入cache擎浴,下次直接從緩存獲取,提高調用效率毒涧。

objc_ivar_list

objc_ivar_list 結構體存儲objc_ivar(成員變量)數(shù)組列表

struct objc_ivar {
    char * _Nullable ivar_name                               OBJC2_UNAVAILABLE; // 命名
    char * _Nullable ivar_type                               OBJC2_UNAVAILABLE; // 類型
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}    

struct objc_ivar_list {
    int ivar_count                                           OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

objc_method_list

objc_method_list結構體存儲objc_method(方法)數(shù)組列表

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

struct objc_method_list {
    struct objc_method_list * _Nullable obsolete             OBJC2_UNAVAILABLE;

    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}  

objc_cache

struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method _Nullable buckets[1]                              OBJC2_UNAVAILABLE;
};
  1. mask: 指定分配緩存bucket的總數(shù)贮预。runtime 使用這個字段確定線性查找的索引位置
  2. occupied: 實際占用緩存 bucket的總數(shù)
  3. 指向Method數(shù)據(jù)結構指針的數(shù)組,總數(shù)不能超過mask+1契讲;指針可能為空仿吞,這標識緩存bucket沒有被占用,數(shù)組隨著時間增長

Meta Class & Super Class

提供一個實例捡偏,來體感metaclasssuperclass

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

typedef struct mtm_objc_class *mClass;
struct mtm_objc_class {
    mClass ISA;
    mClass superclass;
};

void testMetaClass(id self, SEL _cmd) {
    NSLog(@"This object is %p", self);
    NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);
    
    Class currentClass = [self class];
    for (int i = 0; i < 4; i++) {
        NSLog(@"Following the [isa] pointer %d timer gives %p, class name is %@", i, currentClass, NSStringFromClass(currentClass));
        currentClass = object_getClass(currentClass);
    }
    
    mClass testClass = (__bridge mClass)[self superclass];
    for (int i = 0; i < 4; i++) {
        NSLog(@"Following the [super] pointer %d timer gives %p", i, testClass);
        testClass = testClass ? testClass->superclass : nil;
    }
    NSLog(@"NSObject's class is %p", [NSObject class]);
    Class meta_NSObject = object_getClass([NSObject class]);
    NSLog(@"NSobject's meta class is %p, meta super class is %p", meta_NSObject, ((__bridge mClass)meta_NSObject)->superclass);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Class newClass = objc_allocateClassPair([NSURL class], "TestClass", 0);
        class_addMethod(newClass, @selector(testHandler), (IMP)testMetaClass, "v@:");
        objc_registerClassPair(newClass);
        id instance = [[newClass alloc] initWithString:@"http://www.baidu.com"];
        [instance performSelector:@selector(testHandler)];
    }
    return 0;
}

結果如下

ObjcTest[74791:1048449] This object is 0x100784c00
ObjcTest[74791:1048449] Class is TestClass, super class is NSURL
ObjcTest[74791:1048449] Following the [isa] pointer 0 timer gives 0x100784420, class name is TestClass
ObjcTest[74791:1048449] Following the [isa] pointer 1 timer gives 0x100784450, class name is TestClass
ObjcTest[74791:1048449] Following the [isa] pointer 2 timer gives 0x1003310f0, class name is NSObject
ObjcTest[74791:1048449] Following the [isa] pointer 3 timer gives 0x1003310f0, class name is NSObject
ObjcTest[74791:1048449] Following the [super] pointer 0 timer gives 0x7fff859d7230
ObjcTest[74791:1048449] Following the [super] pointer 1 timer gives 0x100331140
ObjcTest[74791:1048449] Following the [super] pointer 2 timer gives 0x0
ObjcTest[74791:1048449] Following the [super] pointer 3 timer gives 0x0
ObjcTest[74791:1048449] NSObject's class is 0x100331140
ObjcTest[74791:1048449] NSobject's meta class is 0x1003310f0, meta super class is 0x100331140

把日志中的地址放到圖里唤冈,應該就更好理解 isa 鏈表super 鏈表

class_isa_super_class.png

小測試

來看看能答對幾個?

BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];  
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  
BOOL res3 = [(id)[NSURL class] isKindOfClass:[NSURL class]];  
BOOL res4 = [(id)[NSURL class] isMemberOfClass:[NSURL class]];

解密開始

  1. 誤區(qū):isKindOfClass有類方法和對象方法银伟,實現(xiàn)有區(qū)別
  2. res1:第一次你虹,Class 和 MetaClass(Class->ISA)對比,失斖堋傅物;再跟NSObject's meta class 的 superclass 即 NSObject class,結果為true
  3. res2:Class->ISA == Class 為false琉预,下面依此類推
  4. res3 = false
  5. res4 = false
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末董饰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子圆米,更是在濱河造成了極大的恐慌卒暂,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娄帖,死亡現(xiàn)場離奇詭異也祠,居然都是意外死亡,警方通過查閱死者的電腦和手機近速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門诈嘿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人数焊,你說我怎么就攤上這事永淌∑槌。” “怎么了佩耳?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谭跨。 經(jīng)常有香客問我干厚,道長李滴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任蛮瞄,我火速辦了婚禮所坯,結果婚禮上,老公的妹妹穿的比我還像新娘挂捅。我一直安慰自己芹助,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布闲先。 她就那樣靜靜地躺著状土,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伺糠。 梳的紋絲不亂的頭發(fā)上蒙谓,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音训桶,去河邊找鬼累驮。 笑死,一個胖子當著我的面吹牛舵揭,可吹牛的內容都是我干的谤专。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼午绳,長吁一口氣:“原來是場噩夢啊……” “哼毒租!你這毒婦竟也來了?” 一聲冷哼從身側響起箱叁,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤墅垮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后耕漱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體算色,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年螟够,在試婚紗的時候發(fā)現(xiàn)自己被綠了灾梦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡妓笙,死狀恐怖若河,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情寞宫,我是刑警寧澤萧福,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站辈赋,受9級特大地震影響鲫忍,放射性物質發(fā)生泄漏膏燕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一悟民、第九天 我趴在偏房一處隱蔽的房頂上張望坝辫。 院中可真熱鬧,春花似錦射亏、人聲如沸近忙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽银锻。三九已至,卻和暖如春做鹰,著一層夾襖步出監(jiān)牢的瞬間击纬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工钾麸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留更振,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓饭尝,卻偏偏與公主長得像肯腕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钥平,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • 本文轉載自:http://southpeak.github.io/2014/10/25/objective-c-r...
    idiot_lin閱讀 935評論 0 4
  • Objective-C語言是一門動態(tài)語言实撒,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了運行時來處理。這種動態(tài)語言的...
    有一種再見叫青春閱讀 585評論 0 3
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言涉瘾,那么這個「動態(tài)」表現(xiàn)在哪呢知态?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,195評論 0 7
  • 本文詳細整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼立叛,具備了靈活的...
    lylaut閱讀 800評論 0 4
  • 本文轉載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 763評論 0 1