對象探索-初探isa

知識要點:

Clang簡介
對象本質(zhì)
類和對象如何關(guān)聯(lián)(initIsa)

Clang

Clang是一個C語言、C++捎琐、Objective-C語言的輕量級編譯器。源代碼發(fā)布于BSD協(xié)議下砸彬。 Clang將支持其普通lambda表達(dá)式造垛、返回類型的簡化處理以及更好的處理constexpr關(guān)鍵字。

Clang是一個由Apple主導(dǎo)編寫姻乓,基于LLVM C/C++/Objective-C編譯器

2013年4月,Clang已經(jīng)全面支持C++11標(biāo)準(zhǔn)嵌溢,并開始實現(xiàn)C++1y特性(也就是C++14,這是 C++的下一個小更新版本)蹋岩。Clang將支持其普通lambda表達(dá)式赖草、返回類型的簡化處理以及更 好的處理constexpr關(guān)鍵字。

Clang是一個C++編寫剪个、基于LLVM秧骑、發(fā)布于LLVM BSD許可證下的C/C++/Objective-C/ Objective-C++編譯器。它與GNU C語言規(guī)范幾乎完全兼容(當(dāng)然扣囊,也有部分不兼容的內(nèi)容乎折, 包括編譯命令選項也會有點差異),并在此基礎(chǔ)上增加了額外的語法特性侵歇,比如C函數(shù)重載 (通過attribute((overloadable))來修飾函數(shù))骂澄,其目標(biāo)(之一)就是超越GCC。

對象本質(zhì)

定義一個對象惕虑,使用clang -rewrite-objc main.m -o main.cpp 把目標(biāo)文件編譯成c++文件
編譯時可能會遇到以下問題:
UIKit報錯問題
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m

xcode安裝的時候順帶安裝了xcrun命令酗洒,xcrun命令在clang的基礎(chǔ)上進(jìn)行了 一些封裝,要更好用一些
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (模擬器)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp (手機(jī))

編譯之后枷遂,你會看到上萬行代碼樱衷,不要慌,直接搜索類名酒唉,找到如下代碼(在Mac上調(diào)試):

#ifndef _REWRITER_typedef_Person
#define _REWRITER_typedef_Person
typedef struct objc_object Person;
typedef struct {} _objc_exc_Person;
#endif

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

結(jié)論:對象的本質(zhì)是結(jié)構(gòu)體

類到底是如何被關(guān)聯(lián)的

alloc與init探索中說到矩桂,類與開辟的空間是通過initInstanceIsa關(guān)聯(lián)起來的,但是,沒有詳細(xì)探索侄榴。上源碼:

// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa 
// field as an index into a class table.
// Note, keep this in sync with any .s files which also define it.
// Be sure to edit objc-abi.h as well.
#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#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
        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}
1.為了探索需要雹锣,先看看isa的定義:
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
};

//ISA_BITFIELD定義
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif
思考

apple在定義isa時,為什么要考慮使用聯(lián)合體癞蚕,而不使用結(jié)構(gòu)體?
我們知道蕊爵,現(xiàn)在幾乎都支持64位,64位可以存儲2的64次方個數(shù)據(jù)桦山。但是一個類的基本信息用這64位完全夠用攒射,不需要定義一個結(jié)構(gòu)體,在結(jié)構(gòu)體中定義一個個成員來存儲類的信息恒水。這樣做会放,大大減少了內(nèi)存,畢竟一個工程所創(chuàng)建的類還是比較多的钉凌。

注意:
isa是存儲類的信息咧最,并不包括屬性、方法等御雕,這些信息在其Class中

2.接下來矢沿,我們進(jìn)行驗證

2.1)定義一個Person類

@interface Person : NSObject

@end

#import "Person.h"

@implementation Person

@end

2.2)在之前配置好的objc源碼中,找到mian函數(shù)酸纲,引入Person并初始化捣鲸,下斷點:


Person.png

2.3)在initInstanceIsa、initIsa方法中斷點


initIsa.png

注意:開始運(yùn)行前福青,這兩個斷點先去掉,等走到Person的斷點位置脓诡,再放開這兩個斷點无午,會進(jìn)入initIsa:


image.png

2.4)斷點繼續(xù)走,我們看到給isa賦初始值:


isa初始值.png

2.5)輸出isa:


輸出isa.png

3.見證奇跡
3.1)ISA_MAGIC_VALUE是:0x001d800000000001ULL祝谚,轉(zhuǎn)換為二進(jìn)制:


ISA_MAGIC_VALUE.png

3.2)將ISA_MAGIC_VALUE轉(zhuǎn)換為二進(jìn)制宪迟,isa賦值之后,根據(jù)ISA_BITFIELD(isa位域)對應(yīng)的位置表示:

image.png

3.3)乍一看交惯,覺得cls和bits不同次泽,違反了聯(lián)合體的互斥原則,其實是相同的席爽,只不過一個是16機(jī)制表示意荤,一個是10進(jìn)制表示。將上圖的值與isa打印的值比較只锻,是一一對應(yīng)的
3.4)每一個位域表示的含義
nonpointer:表示是否對 isa 指針開啟指針優(yōu)化 0:純isa指針玖像,1:不止是類對象地址,isa 中包含了類信息、對象的引用計數(shù)等
has_assoc:關(guān)聯(lián)對象標(biāo)志位齐饮,0沒有捐寥,1存在
has_cxx_dtor:該對象是否有 C++ 或者 Objc 的析構(gòu)器,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯, 如果沒有,則可以更快的釋放對象
shiftcls:存儲類指針的值笤昨。開啟指針優(yōu)化的情況下,在 arm64 架構(gòu)中有 33 位用來存儲類指針握恳。
extra_rc:當(dāng)表示該對象的引用計數(shù)值瞒窒,實際上是引用計數(shù)值減 1, 例如乡洼,如果對象的引用計數(shù)為 10崇裁,那么 extra_rc 為 9。如果引用計數(shù)大于 10就珠, 則需要使用到下面的 has_sidetable_rc寇壳。
magic:用于調(diào)試器判斷當(dāng)前對象是真的對象還是沒有初始化的空間
weakly_referenced:志對象是否被指向或者曾經(jīng)指向一個 ARC 的弱變量,沒有弱引用的對象可以更快釋放妻怎。
deallocating:標(biāo)志對象是否正在釋放內(nèi)存
has_sidetable_rc:當(dāng)對象引用技術(shù)大于 10 時壳炎,則需要借用該變量存儲進(jìn)位
3.5)對shiftcls賦值
shiftcls.png

(uintptr_t)cls是將cls轉(zhuǎn)為10進(jìn)制,我們再次打印isa:
image.png

為什么要右移3位?是要把位域中的最后三位抹除逼侦,驗證:
既然右移3位將第三位抹零匿辩,因為shitcls占44位,左移20位榛丢,將高20位抹零铲球。由于先右移3后左移20,所以晰赞,需要右移17位回歸到原位置稼病,然后輸出,你發(fā)現(xiàn)他就是Person:


image.png

可能你對左移右移迷糊掖鱼,看下圖


i移位.png

Apple早就想到移來移去太麻煩然走,定義了一個掩碼ISA_MASK,直接將isa的shitcls與上掩碼(高17位低3位都是0戏挡,中間44位是1)芍瑞,就可得到cls:


cls.png
思考

isa明明是一個聯(lián)合體,為什么在對象中是一個Class類型褐墅?

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

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

其實拆檬,本質(zhì)是一樣的。我們在使用的時候妥凳,都是Class竟贯,這是對我們直接訪問類的屬性或方法是方便的,所以逝钥,獲取類時會將isa的聯(lián)合體轉(zhuǎn)換為Class

inline Class 
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
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澄耍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌齐莲,老刑警劉巖痢站,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異选酗,居然都是意外死亡阵难,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門芒填,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呜叫,“玉大人,你說我怎么就攤上這事殿衰≈烨欤” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵闷祥,是天一觀的道長娱颊。 經(jīng)常有香客問我,道長凯砍,這世上最難降的妖魔是什么箱硕? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮悟衩,結(jié)果婚禮上剧罩,老公的妹妹穿的比我還像新娘。我一直安慰自己座泳,他們只是感情好惠昔,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挑势,像睡著了一般镇防。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薛耻,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天营罢,我揣著相機(jī)與錄音赏陵,去河邊找鬼饼齿。 笑死,一個胖子當(dāng)著我的面吹牛蝙搔,可吹牛的內(nèi)容都是我干的缕溉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼吃型,長吁一口氣:“原來是場噩夢啊……” “哼证鸥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤枉层,失蹤者是張志新(化名)和其女友劉穎泉褐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸟蜡,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡膜赃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揉忘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跳座。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泣矛,靈堂內(nèi)的尸體忽然破棺而出疲眷,到底是詐尸還是另有隱情,我是刑警寧澤您朽,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布狂丝,位于F島的核電站,受9級特大地震影響虚倒,放射性物質(zhì)發(fā)生泄漏美侦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一魂奥、第九天 我趴在偏房一處隱蔽的房頂上張望菠剩。 院中可真熱鬧,春花似錦耻煤、人聲如沸具壮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棺妓。三九已至,卻和暖如春炮赦,著一層夾襖步出監(jiān)牢的瞬間怜跑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工吠勘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留性芬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓剧防,卻偏偏與公主長得像植锉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子峭拘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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