iOS - isa指針

[toc]

參考

isa指針

isa 簡(jiǎn)介

isa (is a kind of) 是一個(gè) Class 類型的指針, 而 Classstruct objc_class * 的別名, 所以 isa 實(shí)際上是指向 objc_class 類型結(jié)構(gòu)體 的指針捆蜀。

可以理解為是誰的實(shí)例

isa 指向

  • instance 的 isa 指向 class

    • 當(dāng)調(diào)用對(duì)象方法時(shí)洛波,通過instance的isa找到class愤惰,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
  • class 的 isa 指向 meta-class

    • 當(dāng)調(diào)用類方法時(shí),通過class的isa找到meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用
  • 根類的isa指向根元類, 而根元類也繼承自根類

<img src="https://cdn.jsdelivr.net/gh/coder-felix/image/20200605155514.png" style="zoom:50%;" />

isa 地址

查看實(shí)例對(duì)象 isa 指向的地址

查看的是 struct objc_object 的 isa 指針

(lldb)p person->isa // 控制臺(tái)輸出 person 所屬的類 QGPerson

(lldb)p/x (long)person->isa // 控制臺(tái)輸出 isa 的十六進(jìn)制地址值
查看類對(duì)象 isa 指向的地址

查看的是 struct objc_class 的 isa 指針

因?yàn)轭悓?duì)象的isa沒有暴露出來, 所以 p/x personClass->isa 這樣是查看不到的

// 根據(jù)系統(tǒng)的 objc_class 新建一個(gè)結(jié)構(gòu)體, 用于指向 personClass
struct qg_objc_class {
    Class isa;
    Class superclass;
};

// OC對(duì)象橋接給C結(jié)構(gòu)體
struct qg_objc_class *personClass = (__bridge struct qg_objc_class *)([Person class]);
p/x (long)personClass -> isa // 
真實(shí)地址需要位運(yùn)算

從 64bit 開始,isa需要進(jìn)行一次位運(yùn)算,由 isa & ISA_MASK 計(jì)算出真實(shí)地址。

注: superclass 指針不需要這個(gè)位運(yùn)算, 直接指向父class地址

<img src="https://cdn.jsdelivr.net/gh/coder-felix/image/20200605175643.png" style="zoom: 50%;" />

image

isa 底層 ★

基礎(chǔ)知識(shí)分解

共用體 + 掩碼 + 位域

實(shí)現(xiàn)內(nèi)存優(yōu)化


#import "Person.h"

#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
#define ThinMask (1<<3)

@interface Person()
{
    // 共用體
    // 大家共用一塊內(nèi)存, 共用這一個(gè)字節(jié)
    // 存取采用位運(yùn)算, 使用位域增加可讀性
    union {
        // 外界訪問的是 bits
        char bits;
        // 位域
        // 在共用體這里, 僅僅是為了增加代碼可讀性, 沒有實(shí)質(zhì)作用, 不影響存儲(chǔ)
        // 這個(gè)結(jié)構(gòu)體與 bits 共用一個(gè)字節(jié)
        struct {
            char tall : 1; // : 1 代表只占1位, 與前面的char類型無關(guān), 這個(gè)存儲(chǔ)在最低位 ★
            char rich : 1;
            char handsome : 1;
            char thin : 1;
        };
    } _tallRichHandsome;
}
@end

@implementation Person

// 使用位運(yùn)算, 更高效
- (void)setTall:(BOOL)tall {
    if (tall) { // 1 按位或
        _tallRichHandsome.bits |= TallMask;
    } else { // 0  按位取反, 再按位與
        _tallRichHandsome.bits &= ~TallMask;
    }
}

- (BOOL)isTall {
    return !!(_tallRichHandsome.bits & TallMask);
}

- (void)setRich:(BOOL)rich {
    if (rich) {
        _tallRichHandsome.bits |= RichMask;
    } else {
        _tallRichHandsome.bits &= ~RichMask;
    }
}

- (BOOL)isRich {
    return !!(_tallRichHandsome.bits & RichMask);
}

- (void)setHandsome:(BOOL)handsome {
    if (handsome) {
        _tallRichHandsome.bits |= HandsomeMask;
    } else {
        _tallRichHandsome.bits &= ~HandsomeMask;
    }
}

- (BOOL)isHandsome {
    return !!(_tallRichHandsome.bits & HandsomeMask);
}


- (void)setThin:(BOOL)thin {
    if (thin) {
        _tallRichHandsome.bits |= ThinMask;
    } else {
        _tallRichHandsome.bits &= ~ThinMask;
    }
}

- (BOOL)isThin {
    return !!(_tallRichHandsome.bits & ThinMask);
}

@end


isa union ★

在 arm64 架構(gòu)之前, isa就是一個(gè)普通的指針, 存儲(chǔ)著 Class喻频、Meta-Class 對(duì)象的內(nèi)存地址;

從 arm64 架構(gòu)開始, 對(duì)isa進(jìn)行了優(yōu)化, 變成了一個(gè) union , 這8個(gè)字節(jié)(64位)不僅僅用來放地址值(其中33位), 還使用位域來存儲(chǔ)更多的信息; ★

isa_t
// objc-private.h
// 共用體
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits; // 主要成員, 通過bits獲取isa的相關(guān)數(shù)據(jù)
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // 位域 defined in isa.h
    };
#endif
};
ISA_BITFIELD
// isa.h
ISA_BITFIELD in arm64: 

// 0: 代表是普通的指針, 存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址
// 1: 代表優(yōu)化過, 使用位域存儲(chǔ)更多的信息
uintptr_t nonpointer        : 1; 
// 是否有設(shè)置過關(guān)聯(lián)對(duì)象(就算清空了, 也是設(shè)置過), 如果沒有, 釋放時(shí)會(huì)更快
uintptr_t has_assoc         : 1; 
// 是否有C++的析構(gòu)函數(shù)(.cxx_destruct), 如果沒有, 釋放時(shí)會(huì)更快
uintptr_t has_cxx_dtor      : 1; 
// 存儲(chǔ)著Class肘迎、Meta-Class對(duì)象的內(nèi)存地址信息
uintptr_t shiftcls          : 33;
// 用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
uintptr_t magic             : 6; 

// 是否有被弱引用指向過(就算清空了, 也是指向過), 如果沒有, 釋放時(shí)會(huì)更快
// 弱指針 weak 存儲(chǔ)在 SideTable 中 ★
uintptr_t weakly_referenced : 1; 
// 對(duì)象是否正在釋放
uintptr_t deallocating      : 1; 

// 引用計(jì)數(shù)器是否過大無法存儲(chǔ)在 isa 中 (大于19bit) ★
// 若為1, 引用計(jì)數(shù)會(huì)存儲(chǔ)在單獨(dú)的結(jié)構(gòu)體 SideTable 中
// 若為0, 引用計(jì)數(shù)會(huì)存儲(chǔ)在 isa 的 extra_rc 中
uintptr_t has_sidetable_rc  : 1; 

// 里面存儲(chǔ)的值是引用計(jì)數(shù)器減1
uintptr_t extra_rc          : 19
ISA()
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
}

// arm64
#define ISA_MASK        0x0000000ffffffff8ULL 
// __x86_64__
#define ISA_MASK        0x00007ffffffffff8ULL // shiftcls : 44

ISA_MASK 對(duì)應(yīng)二進(jìn)制是 0b111...111000 (33個(gè)1 + 3個(gè)0)
所以, 類對(duì)象, 元類對(duì)象的地址值, 最后三位永遠(yuǎn)是0 (對(duì)應(yīng)十六進(jìn)制, 最后一位, 只可能是0或8)

SideTable

// NSObject.mm
struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts; // 一個(gè)存放著對(duì)象引用計(jì)數(shù)的散列表

    weak_table_t weak_table; // 弱引用表

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
RefcountMap
// RefcountMap disguises its pointers because we 
// don't want the table to act as a root for `leaks`.
typedef objc::DenseMap<DisguisedPtr<objc_object>, size_t, RefcountMapValuePurgeable> RefcountMap;
weak_table_t

見《weak

weak_entry_t

見《weak

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甥温,一起剝皮案震驚了整個(gè)濱河市锻煌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姻蚓,老刑警劉巖宋梧,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狰挡,居然都是意外死亡捂龄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門加叁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倦沧,“玉大人,你說我怎么就攤上這事它匕≌谷冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵超凳,是天一觀的道長(zhǎng)愈污。 經(jīng)常有香客問我耀态,道長(zhǎng)轮傍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任首装,我火速辦了婚禮创夜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仙逻。我一直安慰自己驰吓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布系奉。 她就那樣靜靜地躺著檬贰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缺亮。 梳的紋絲不亂的頭發(fā)上翁涤,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音萌踱,去河邊找鬼葵礼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛并鸵,可吹牛的內(nèi)容都是我干的鸳粉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼园担,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼届谈!你這毒婦竟也來了枯夜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤疼约,失蹤者是張志新(化名)和其女友劉穎卤档,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體程剥,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劝枣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了织鲸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舔腾。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搂擦,靈堂內(nèi)的尸體忽然破棺而出稳诚,到底是詐尸還是另有隱情,我是刑警寧澤瀑踢,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布扳还,位于F島的核電站,受9級(jí)特大地震影響橱夭,放射性物質(zhì)發(fā)生泄漏氨距。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一棘劣、第九天 我趴在偏房一處隱蔽的房頂上張望俏让。 院中可真熱鬧,春花似錦茬暇、人聲如沸首昔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勒奇。三九已至,卻和暖如春巧骚,著一層夾襖步出監(jiān)牢的瞬間赊颠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工网缝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巨税,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓粉臊,卻偏偏與公主長(zhǎng)得像草添,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扼仲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355