iOS底層 - 對(duì)象的本質(zhì)&isa分析

前言

OC是一個(gè)面向?qū)ο蟮木幊陶Z(yǔ)言东涡,對(duì)象就是我們整個(gè)編寫(xiě)代碼的過(guò)程中,最為頻繁接觸到的一個(gè)東西,那么什么是對(duì)象呢霍弹?在上一篇文章iOS底層 - 結(jié)構(gòu)體內(nèi)存對(duì)齊中,我們了解到:對(duì)象的本質(zhì)就是結(jié)構(gòu)體娃弓。那么這個(gè)結(jié)論怎么驗(yàn)證呢典格?那么下面開(kāi)始一探究竟。

一.了解clang

在探究對(duì)象本質(zhì)之前先介紹一下clang:

  • Clang是?個(gè)C語(yǔ)?台丛、C++耍缴、Objective-C語(yǔ)?的輕量級(jí)編譯器
  • Clang將?持其普通lambda表達(dá)式、返回類(lèi)型的簡(jiǎn)化處理以及更好的處理constexpr關(guān)鍵字
  • Clang是?個(gè)由Apple主導(dǎo)編寫(xiě)挽霉,基于LLVM的C/C++/Objective-C編譯器
  • 2013年4?,Clang已經(jīng)全??持C++11標(biāo)準(zhǔn)防嗡,并開(kāi)始實(shí)現(xiàn)C++1y特性(也就是C++14,這是C++的下?個(gè)?更新版本)
  • Clang是?個(gè)C++編寫(xiě)侠坎、基于LLVM蚁趁、發(fā)布于LLVM BSD許可證下的C/C++/Objective-C/Objective-C++編譯器。

二.編譯oc文件為c++文件

  1. 直接命令行編譯 :
//  把?標(biāo)?件編譯成c+
clang -rewrite-objc main.m -o main.cpp 
  1. xcode安裝的時(shí)候順帶安裝了xcrun命令实胸,xcrun命令在clang的基礎(chǔ)上進(jìn)?了?些封裝他嫡,要更好??些:
//  模擬器
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite- objc main.m -o main-arm64.cpp 
//  真機(jī)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main arm64.cpp 
  1. 成功生成如下文件:


三.分析c++文件

開(kāi)發(fā)main.cpp文件,搜索LGPerson

LGPerson

上圖所示庐完,我們可以看到對(duì)象在底層的本質(zhì)是一個(gè)結(jié)構(gòu)體钢属。

跟蹤NSObject_IMPL結(jié)構(gòu)類(lèi)型可以看到如圖下:


isa
objc

上圖所示,可以得出objc底層調(diào)用就是objc_object

  • id class = [class new] 為什么我們id 類(lèi)型可以獲取所有的屬性類(lèi)型而且不需要加门躯,因?yàn)樗牡讓泳褪莍d
@property (nonatomic, strong) NSString *KCName;
  • 為什么屬性自帶set 和get 方法 根據(jù)底層跟蹤如下:
// @implementation LGPerson
// 方法 getter
static NSString * _I_LGPerson_kcName(LGPerson * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName))
}

static void _I_LGPerson_setKcName_(LGPerson * self, SEL _cmd, NSString *kcName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName)) = kcName; }
// @end
  • 總結(jié):對(duì)象本質(zhì)就是一個(gè)結(jié)構(gòu)體淆党,屬性成員變量實(shí)現(xiàn)了 get 和 set 方法.
  • 流程分析 :LGPerson —> 找到LGPerson_IMPL —> NSObject_IMPL -—> Class

四.isa分析

struct NSObject_IMPL {
    Class isa;
};

在上面的代碼里,我們可以看到NSObject里面只有一個(gè)成員變量,那就是Class類(lèi)型的isa宁否。那么這isa是什么呢窒升,我們就這個(gè)問(wèn)題,繼續(xù)探索下去慕匠,首先我們先看看他的類(lèi)型Class在底層中的定義饱须。

typedef struct objc_class *Class;

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;

可以看到,Class實(shí)際上就是一個(gè)objc_class *類(lèi)型的結(jié)構(gòu)體指針台谊。

接下來(lái)我們來(lái)看看isa蓉媳,在alloc流程的最后一步,就是通過(guò)initIsa方法將我們申請(qǐng)的內(nèi)存地址和我們的Class綁定起來(lái)锅铅。

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);
    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());
#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
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }
    isa = newisa;
}

在這些代碼中間酪呻,有個(gè)非常重要的東西,就是isa_t盐须,我們?cè)賮?lái)看看它到底是什么:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    uintptr_t bits;

private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;

public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

    bool isDeallocating() {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    void setDeallocating() {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif

    void setClass(Class cls, objc_object *obj);
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated);
};

五.結(jié)構(gòu)體和聯(lián)合體

在上面的代碼中玩荠,我們可以看到一個(gè)之前沒(méi)有接觸過(guò)的結(jié)構(gòu)union,我們稱之為聯(lián)合體贼邓,那么他到底是什么阶冈,有什么特性呢,我們用下面這個(gè)例子來(lái)說(shuō)明塑径,首先看一段代碼

// 結(jié)構(gòu)體 : 共存
struct XHTeacher1 {
    char        *name;
    int         age;
    double      height ;
};

// 聯(lián)合體 : 互斥
union XHTeacher2 {
    char        *name;
    int         age;
    double      height ;
};
  • 結(jié)構(gòu)體(sturct)中的所有變量是“共存”的
    優(yōu)點(diǎn):海納百川女坑,有容乃大。只要你來(lái)统舀,我都給你存下來(lái)
    缺點(diǎn):內(nèi)存空間的分配是粗放的匆骗,不管你用不用全都給你分配好位置

  • 聯(lián)合體(union)中每個(gè)變量之間是“互斥”的
    優(yōu)點(diǎn):就是不夠“包容”
    缺點(diǎn):使用內(nèi)存更為精細(xì)靈活,也節(jié)省了內(nèi)存空間

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末誉简,一起剝皮案震驚了整個(gè)濱河市碉就,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闷串,老刑警劉巖铝噩,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窿克,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)毛甲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)年叮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人玻募,你說(shuō)我怎么就攤上這事只损。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵跃惫,是天一觀的道長(zhǎng)叮叹。 經(jīng)常有香客問(wèn)我,道長(zhǎng)爆存,這世上最難降的妖魔是什么蛉顽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮先较,結(jié)果婚禮上携冤,老公的妹妹穿的比我還像新娘。我一直安慰自己闲勺,他們只是感情好曾棕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著菜循,像睡著了一般翘地。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上癌幕,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天衙耕,我揣著相機(jī)與錄音,去河邊找鬼序芦。 笑死臭杰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谚中。 我是一名探鬼主播渴杆,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宪塔!你這毒婦竟也來(lái)了磁奖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤某筐,失蹤者是張志新(化名)和其女友劉穎比搭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體南誊,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡身诺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抄囚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霉赡。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖幔托,靈堂內(nèi)的尸體忽然破棺而出穴亏,到底是詐尸還是另有隱情蜂挪,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布嗓化,位于F島的核電站棠涮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刺覆。R本人自食惡果不足惜严肪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隅津。 院中可真熱鬧诬垂,春花似錦、人聲如沸伦仍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)充蓝。三九已至隧枫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谓苟,已是汗流浹背官脓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涝焙,地道東北人卑笨。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仑撞,于是被迫代替她去往敵國(guó)和親赤兴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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