iOS底層原理之isa分析

1.isa本質

我們知道,oc中的一切類都繼承自NSObject,直接追蹤NSObject可以發現在objc/NSObject.h文件中對于該類的定義如下

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

或者在objc源碼中找到關于類在C語言層面的定義

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
  • 不難發現,每一個類的初始屬性都是一個名為isa的指針,可以看出,isa的類型為Class,而Class則代表著一切類,繼續查看Class的類型,發現Class其實是一個名為object_class的結構體指針,里面定義一些常用成員,所以初步得出isa就是一個類指針,里面存放著類的地址
typedef struct objc_class *Class;
  • 繼續追蹤objc_class,發現其繼承自obj_object,實際上在舊版的代碼中,objc_class是一個單純的結構體,在new版本的代碼中才繼承自obj_object,這也符合了面向對象中,萬物皆對象的理念。在objc-runtime-new.h文件中可以看到,之后尋找objc_object的初始定義,最終在objc-private.h文件中發現了最初始的object定義以及isa的詳細結構,isa是一個聯合體位域,里面關聯了它所指向的類,也就是Class cls
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

    class_rw_t *data() { 
        return bits.data();
    }
}
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};


struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
}

2.isa初始化

  • 在之前的alloc流程中,我們發現,在執行class_createInstance方法的時候,會調用 obj->initIsa(cls) 方法進行isa的初始化,其中cls代表當前的類,
  • 當nonpointer = 0,代表當前為普通指針,那么直接通過isa.cls和類進行關聯
  • 當nonpointer = 1的時候,代表是優化過的isa指針,會使用位域存放更多的信息,此時創建newisa并初始化值后賦值給isa指針
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
    // 此處有一些代碼,不過不重要,暫時不會走這里
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        isa = newisa;
    }
}

3.isa對類和對象的關聯

  • 下面探索一下,isa是否已經和當前的類進行了關聯,先通過object_getClass方法得出結論,獲取對象的類其實就是返回當前對象的isa指針,那么就可以通過isa指針找出所屬的類
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    // 一般都不是TaggedPointer,這是特殊指針
    if (!isTaggedPointer()) return ISA();
}

objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    // 一般情況下走這里,獲取到類
    return (Class)(isa.bits & ISA_MASK);
#endif
}
// 64位架構下 ISA_MASK的值為
#   define ISA_MASK        0x00007ffffffffff8ULL
  • 進行試驗,先通過x/4gx打印出當前對象的地址以及值,其中第一個位置必然為isa指針,那么就可以用p/x先輸出類的地址,再通過isa & ISA_MASK找出isa對應的類地址,進行比對發現兩者一致,所以得出結論isa關聯了對象和類,對象的isa指針指向該對象所屬的類,流程記錄如下圖

    image.png

4.類中isa的指向

    1. 剛才驗證了isa對于類和對象的關聯,由于class繼承自object,萬物皆Object,那么繼續剛才的試驗,驗證一下類中的isa指向,廢話不多說,直接上圖
    image.png

問題來了,按照剛才的步驟打印出了類的地址,但是通過isa指針尋找之后得到地址0x0000000100001108,并且打印值和之前打印類一樣均為LGPerson,但是二者的地址卻截然不同,我們知道,類在內存中只可能存在一份,對象可以存在多份,那么可以推斷,當前0x0000000100001108是一個和LGPerson名字相同但是卻不一樣的類,姑且認為它是 LG-A

    1. 繼續按照步驟驗證下去,終于發現值變了,此次isa指針尋找到的類發生了變化,新找到的類名為NSObject,但是任然存在剛才的問題,這個NSObject和我們直接打印的NSObject的地址并不相同,所以暫定它為NSObject-A

      image.png
    1. 繼續重復步驟,發現最終NSObject-A類的isa指針一直指向自己,并且還發現有意思的是,第二個指針指向了NSObject類,其實這第二個指針就是類中的supercClass指針,所以NSObject-A類的父類其實就是NSObject類。
    image.png

總結:

  • 到此為止isa的本質,初始化以及isa在對象,類中的指向圖已經分析完畢,在我們分析中所提到的NSObject-A,NSObject-A其實又叫做元類,其中NSObject的元類又叫做根源類,它是一切元類的父類,所有元類的isa指針全部指向根源類,最后回歸到萬物皆對象,所以根源類的父類則是NSObject,而NSObject的父類則為nil,這里本來應該有一副很經典的isa和父類繼承路線圖,但是可能對于很多人來說理解不是很容易,所以我上了一個漢化版本
    image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容