日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

iOS之深入解析对象isa的底层原理

發(fā)布時(shí)間:2024/5/28 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS之深入解析对象isa的底层原理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

對(duì)象本質(zhì)

一、NSObject 本質(zhì)

OC代碼的底層實(shí)現(xiàn)實(shí)質(zhì)是 C/C++代碼 ,繼而編譯成匯編代碼,最終變成機(jī)器語(yǔ)言。

① clang C/C++ 編譯器
  • Clang 是?個(gè) C 語(yǔ)?、C++、Objective-C 語(yǔ)?的 輕量級(jí)編譯器 ,源代碼發(fā)布于 BSD 協(xié)議下。
  • 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 將?持其 普通lambda表達(dá)式 ,返回類(lèi)型的簡(jiǎn)化處理以及更好的處理 constexpr關(guān)鍵字
  • Clang 是?個(gè) C++ 編寫(xiě),基于 LLVM 發(fā)布于 LLVM BSD 許可證下的 C/C++/Objective-C/Objective-C++ 編譯器。它與 GNU C 語(yǔ)?規(guī)范?乎完全兼容(當(dāng)然也有部分不兼容的內(nèi)容,包括編譯命令選項(xiàng)也會(huì)有點(diǎn)差異),并在此基礎(chǔ)上增加了額外的語(yǔ)法特性,?如 C 函數(shù)重載(通過(guò) attribute((overloadable) )來(lái)修飾函數(shù)),其?標(biāo)(之?)就是超越 GCC 。
  • clang -rewrite-objc main.m -o main.cpp 把?標(biāo)?件編譯成c++?件。
  • UIKit報(bào)錯(cuò)問(wèn)題:Xcode安裝的時(shí)候順帶安裝了 xcrun 命令, xcrun 命令在 clang 的基礎(chǔ)上進(jìn)?了?些封裝。
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
  • 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 mainarm64.cpp (?機(jī))
② 運(yùn)用 clang 將目標(biāo)文件編譯成 cpp(C++文件)
  • 在main.m中添加以下代碼:
#import <Foundation/Foundation.h> #import <objc/runtime.h>@interface YDWHandsomeBoy : NSObject @property (nonatomic, copy) NSString *name; @end@implementation YDWHandsomeBoy@endint main(int argc, const char * argv[]) {@autoreleasepool {// insert code here...NSLog(@"Hello, World!");}return 0; }
  • 打開(kāi)終端,進(jìn)入main.m所在的文件夾,通過(guò) clang rewirte-objc main.m -o main.cpp xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 命令,生成cpp文件。

  • 然后回到main.m的文件中,打開(kāi)cpp文件,搜索“YDWHandsomeBoy”,即可看到,對(duì)象YDWHandsomeBoy在底層被編譯成了一個(gè)結(jié)構(gòu)體:
#ifndef _REWRITER_typedef_YDWHandsomeBoy#define _REWRITER_typedef_YDWHandsomeBoytypedef struct objc_object YDWHandsomeBoy;typedef struct {} _objc_exc_YDWHandsomeBoy;#endifextern "C" unsigned long OBJC_IVAR_$_YDWHandsomeBoy$_name;struct YDWHandsomeBoy_IMPL {struct NSObject_IMPL NSObject_IVARS;NSString *_name;};// @property (nonatomic, copy) NSString *name;/* @end */// @implementation YDWHandsomeBoy
  • 可以看到上面的c++代碼中有個(gè)NSObject_IMPL NSObject_IVARS這個(gè)東西,然后搜索NSObject_IMPL可以發(fā)現(xiàn):
struct NSObject_IMPL {Class isa;};
  • 由此可以得出:
    • NSObject的底層實(shí)現(xiàn)實(shí)質(zhì)是一個(gè) 結(jié)構(gòu)體 ,而結(jié)構(gòu)體中的成員 isa是Class類(lèi)型
    • 通過(guò)源碼 typedef struct objc_class *Class 可知它是一個(gè)指針,在64為環(huán)境下指針占8個(gè)字節(jié),而在32位機(jī)下是占4個(gè)字節(jié),因此該結(jié)構(gòu)體占8個(gè)字節(jié)(因?yàn)樵摻Y(jié)構(gòu)體只有一個(gè)成員)。

二、NSObject 對(duì)象內(nèi)存

  • 初始化一個(gè)NSObject并打印:
NSObject *obj = [[NSObject alloc] init];NSLog(@"%zd",malloc_size((__bridge const void *)obj));
  • 可知NSObject對(duì)象占16個(gè)字節(jié)。那么與上文中NSObject結(jié)構(gòu)體中占8個(gè)字節(jié)是否沖突?再次打印:
NSLog(@"%zd",class_getInstanceSize([NSObject class]))
  • 不難發(fā)現(xiàn):獲取NSObject類(lèi)的實(shí)例對(duì)象的成員變量所占用的(內(nèi)存對(duì)齊之后)大小,顯示確實(shí)為8個(gè)字節(jié)。
  • 在objc的源碼中找到 class_getInstanceSize方法,發(fā)現(xiàn)它返回的是 cls->alignedInstanceSize() ,對(duì)它的描述為Class’s ivar size rounded up to a pointer-size boundary意指 返回成員變量占據(jù)的大小 。因此創(chuàng)建一個(gè)NSObject對(duì)象需要分配16個(gè)字節(jié),只是真正利用的只有8個(gè)字節(jié),即isa這個(gè)成員的大小。
  • 查看allocWithZone的源碼發(fā)現(xiàn)它最底層的創(chuàng)建實(shí)例的方法實(shí)際上是調(diào)用了C語(yǔ)言的 calloc方法 ,在該方法中,規(guī)定若 分配的字節(jié)不滿(mǎn)16將把它分配為16個(gè)字節(jié)

三、若一個(gè)YDWHandsomeBoy類(lèi)繼承自NSObject類(lèi),那么YDWHandsomeBoy類(lèi)的對(duì)象占多少內(nèi)存?

  • 新建YDWHandsomeBoy類(lèi),添加成員變量,在main中實(shí)現(xiàn)以下代碼:
#import <Foundation/Foundation.h>#import <objc/runtime.h>#import <malloc/malloc.h>@interface YDWHandsomeBoy : NSObject@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *nickName;@property (nonatomic, assign) int age;@property (nonatomic, assign) int address;@property (nonatomic, assign) int number;@end@implementation YDWHandsomeBoy@endint main(int argc, const char * argv[]) {@autoreleasepool {// insert code here...YDWHandsomeBoy *boy = [[YDWHandsomeBoy alloc] init];boy.name = @"Y";boy.nickName = @"D";boy.age = 18;boy.address = 10;boy.number = 11;NSLog(@"%zd",malloc_size((__bridge const void *)boy));}return 0;}打印結(jié)果如下:2020-09-02 15:44:27.345192+0800 iOS之對(duì)象isa[53982:3621024] 48
  • 通過(guò)以上代碼可以看出:
  • 若一個(gè)類(lèi)繼承自另一個(gè)類(lèi),則它的底層會(huì) 將父類(lèi)的成員變量放在結(jié)構(gòu)體的最前面,此后依次放置本類(lèi)的成員變量 。
  • 而從之前的分析可知,NSObject_IMPL的本質(zhì)就是一個(gè) 裝有成員變量isa的結(jié)構(gòu)體 ,因此,YDWHandsomeBoy類(lèi)對(duì)象所占的內(nèi)存為isa的內(nèi)存8加上YDWHandsomeBoy類(lèi)成員變量所占的空間,若不滿(mǎn)16個(gè)字節(jié),會(huì)強(qiáng)制分配到16個(gè)字節(jié)。
  • 由于 內(nèi)存對(duì)齊 的規(guī)定,結(jié)構(gòu)體的最終大小必須是 最大成員的倍數(shù)

isa

一、isa 簡(jiǎn)介

  • alloc初始化時(shí)不僅 創(chuàng)建對(duì)象并且分配內(nèi)存 ,同時(shí) 初始化 isa 指針屬性
  • Objective-C 對(duì)象在底層本質(zhì)上是 結(jié)構(gòu)體 ,所有的對(duì)象里面都會(huì)包含有一個(gè) isa ,isa 的定義是一個(gè) 聯(lián)合體 isa_t ,isa_t 包含了 當(dāng)前對(duì)象指向類(lèi)的信息 。
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 是一個(gè) 聯(lián)合體,而這其實(shí)是從內(nèi)存管理層面來(lái)設(shè)計(jì)的,因?yàn)槁?lián)合體是 所有成員共享一個(gè)內(nèi)存,聯(lián)合體 內(nèi)存的大小取決于內(nèi)部成員內(nèi)存大小最大的那個(gè)元素
  • 對(duì)于 isa 指針來(lái)說(shuō),就不用額外聲明很多的屬性,直接在 內(nèi)部的 ISA_BITFIELD 保存信息 。
  • 由于聯(lián)合體 屬性間互斥 ,所以 cls 和 bits 在 isa 初始化流程時(shí)是在 兩個(gè)分支 中被賦值的。
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};# 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)
  • isa_t 是一個(gè)聯(lián)合體,聯(lián)合體的特性就是 內(nèi)部所有的成員共用一塊內(nèi)存地址空間 ,也就是說(shuō)isa_t、cls、bits會(huì)共用同一塊內(nèi)存地址空間,這塊 內(nèi)存地址空間大小取決于最大長(zhǎng)度內(nèi)部成員的大小 ,即64位8字節(jié),由此可以知道isa 的所占的內(nèi)存空間大小為8字節(jié)。isa_t聯(lián)合體如下:
struct {uintptr_t indexed : 1;uintptr_t has_assoc : 1;uintptr_t has_cxx_dtor : 1;uintptr_t shiftcls : 44;uintptr_t magic : 6;uintptr_t weakly_referenced : 1;uintptr_t deallocating : 1;uintptr_t has_sidetable_rc : 1;uintptr_t extra_rc : 8;};

二、isa 結(jié)構(gòu)

isa 作為一個(gè)聯(lián)合體,有一個(gè)結(jié)構(gòu)體屬性為 ISA_BITFIELD ,其大小為 8 個(gè)字節(jié),也就是 64 位?;赺_arm64__ 和 x86 64 架構(gòu)如下:

# 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
  • union-isa_t 存儲(chǔ)分配如下:

  • nonpointer: 表示是否對(duì) isa 指針 開(kāi)啟指針優(yōu)化
    0: 純 isa 指針;
    1: 不止是類(lèi)對(duì)象地址, isa 中包含了類(lèi)信息、對(duì)象的引用計(jì)數(shù)等。
  • has_assoc: 關(guān)聯(lián)對(duì)象標(biāo)志位 ,0 沒(méi)有,1 存在。
  • has_cxx_dtor: 該對(duì)象 是否有 C++ 或者 Objc 的析構(gòu)器 ,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯,如果沒(méi)有,則可以更快的釋放對(duì)象。
  • shiftcls: 存儲(chǔ)類(lèi)指針的值 ,開(kāi)啟指針優(yōu)化的情況下,在 arm64 架構(gòu)中有 33 位用來(lái)存儲(chǔ)類(lèi)指針。
  • magic: 用于 調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒(méi)有初始化的空間
  • weakly_referenced: 標(biāo)志對(duì)象是否被指向或者曾經(jīng) 指向一個(gè) ARC 的弱變量 ,沒(méi)有弱引用的對(duì)象可以更快釋放。
  • deallocating: 標(biāo)志對(duì)象 是否正在釋放內(nèi)存 。
  • has_sidetable_rc: 當(dāng)對(duì)象引用技術(shù)大于 10 時(shí),則需要借用該變量 存儲(chǔ)進(jìn)位 。
  • extra_rc: 當(dāng)表示該 對(duì)象的引用計(jì)數(shù)值 ,實(shí)際上是引用計(jì)數(shù)值減 1。 例如,如果對(duì)象的引用計(jì)數(shù)為 10,那么 extra_rc 為 9;如果引用計(jì)數(shù)大于 10, 則需要使用到has_sidetable_rc。

三、isa 初始化

① isa 源碼實(shí)現(xiàn)
  • 在objc的源碼中有isa的初始化方法:
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.cls = cls;} else {assert(!DisableNonpointerIsa);assert(!cls->instancesRequireRawIsa());isa_t newisa(0);#if SUPPORT_INDEXED_ISAassert(cls->classArrayIndex() > 0);newisa.bits = ISA_INDEX_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUEnewisa.has_cxx_dtor = hasCxxDtor;newisa.indexcls = (uintptr_t)cls->classArrayIndex();#elsenewisa.bits = ISA_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUEnewisa.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 instantiationisa = newisa;}}
  • 由于nonpointer傳入的是true,SUPPORT_INDEXED_ISA定義為0,所以可以對(duì)這段代碼簡(jiǎn)化一下:
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { isa_t newisa(0);newisa.bits = ISA_MAGIC_VALUE;newisa.has_cxx_dtor = hasCxxDtor;newisa.shiftcls = (uintptr_t)cls >> 3;isa = newisa;}
② isa 初始化數(shù)據(jù)
  • 可以看到對(duì)bits的賦值ISA_MAGIC_VALUE = 0x001d800000000001ULL,將此轉(zhuǎn)為二進(jìn)制,在結(jié)合isa_t的結(jié)構(gòu)得出如下的isa_t的初始數(shù)據(jù)圖:

  • 對(duì) isa 賦值ISA_MAGIC_VALUE初始化實(shí)際上只是設(shè)置了indexed和magic兩部分的數(shù)據(jù):
    • indexed表示 isa_t 的類(lèi)型 :0表示 raw isa,也就是沒(méi)有結(jié)構(gòu)體的部分,訪問(wèn)對(duì)象的 isa 會(huì)直接返回一個(gè)指向 cls 的指針,也就是在 iPhone 遷移到 64 位系統(tǒng)之前時(shí) isa 的類(lèi)型;1則表示當(dāng)前 isa 不是指針,但是其中也有 cls 的信息,只是其中關(guān)于類(lèi)的指針都是保存在 shiftcls 中。
    • magic 用于 調(diào)試器判斷當(dāng)前對(duì)象是否有初始化空間 。
  • 在設(shè)置indexed和magic的值后會(huì)對(duì)has_cxx_dtor進(jìn)行設(shè)值。has_cxx_dtor表示該對(duì)象是否有 C++ 或者 Objc 的析構(gòu)器 ,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯,如果沒(méi)有,則可以更快的釋放對(duì)象。
newisa.has_cxx_dtor = hasCxxDtor;
  • 將當(dāng)前對(duì)象的類(lèi)指針存放在 shiftcls 中:
newisa.shiftcls = (uintptr_t)cls >> 3;
  • 對(duì)cls的地址右移動(dòng)3位的目的是 為了減少內(nèi)存的消耗 ,因?yàn)轭?lèi)的指針需要按照8字節(jié)對(duì)齊,也就是說(shuō)類(lèi)的指針的大小必定是8的倍數(shù),其二進(jìn)制后三位為0 ,右移三位抹除后面的3位0并不會(huì)產(chǎn)生影響。
③ isa的初始化流程示意

四、isa 關(guān)聯(lián)對(duì)象和類(lèi)

isa 是對(duì)象中的第一個(gè)屬性,這是在繼承的時(shí)候發(fā)生的,要早于對(duì)象的成員變量,屬性列表,方法列表以及所遵循的協(xié)議列表。在 alloc 底層,有一個(gè)方法叫做 initIsa ,這個(gè)方法的作用就是 初始化 isa 聯(lián)合體位域 。上文中我們已經(jīng)看到了這個(gè)方法:

newisa.shiftcls = (uintptr_t)cls >> 3;
① cls 存儲(chǔ)到 isa
  • isa 剛初始化時(shí),還沒(méi)有被賦值,bits 全為空值,p newisa 如下:

  • 繼續(xù)向下執(zhí)行,當(dāng)斷點(diǎn)執(zhí)行到如下位置的時(shí)候,bits 會(huì)被賦上默認(rèn)值(nonpointer = 1,magic = 59),繼續(xù)p newisa如下:

  • 為什么 magic = 59 呢?其實(shí),通過(guò)計(jì)算器可以轉(zhuǎn)換算出:0x001d800000000001 = 59,上面我們已經(jīng) po 0x001d800000000001ULL 了,可以看到這個(gè)默認(rèn)值。
  • 繼續(xù)輸出 bits 二進(jìn)制、輸出 cls 指針二進(jìn)制可以看到:在未設(shè)置 shiftcls 時(shí),bits 從右到左 [3, 46] 位都是0。如下:
(lldb) p/t 8303511812964353(long) $3 = 0b0000000000011101100000000000000000000000000000000000000000000001(lldb) p/t (uintptr_t)cls(uintptr_t) $4 = 0b0000000000000000000000000000000100000000010001110100000000111000(lldb) p/t (uintptr_t)cls >> 3(uintptr_t) $5 = 0b0000000000000000000000000000000000100000000010001110100000000111(lldb)
  • 為什么要右移三位?在 Objective-C 中,類(lèi)的指針是按照字節(jié)(8 bits)對(duì)齊的,也就是說(shuō)類(lèi)指針地址轉(zhuǎn)化成十進(jìn)制后,都是8的倍數(shù),也就是說(shuō),類(lèi)指針地址轉(zhuǎn)化成二進(jìn)制后,后三位都是0。既然是沒(méi)有意義的0,那么在存儲(chǔ)時(shí)就可以省略,用節(jié)省下來(lái)的空間存儲(chǔ)一些其他信息。
  • 當(dāng) bits 被賦值之后,如下:

  • 可以看到,現(xiàn)在的 bits 的 [3, 46] 位正好是之前 cls 指針右移三位的內(nèi)容。
② isa 關(guān)聯(lián)對(duì)象和類(lèi)
  • 通過(guò) LLDB 進(jìn)行調(diào)試打印,就可以知道一個(gè)對(duì)象的 isa 會(huì)關(guān)聯(lián)到這個(gè)對(duì)象所屬的類(lèi):

  • LLDB 調(diào)試的時(shí)候左移右移操作其實(shí)很好理解,先觀察 isa 的 ISA_BITFIELD 位域的結(jié)構(gòu):ISA_BITFIELD 的前 3 位是 nonpointer、has_assoc、has_cxx_dtor ,中間 44 位是 shiftcls ,后面 17 位是剩余的內(nèi)容,同時(shí)因?yàn)?iOS 是 小端模式 ,那么就需要去掉右邊的 3 位和左邊的 17位,所以就會(huì)采用 >> 3 << 3 然后 << 13 >> 13 的操作。

五、isa 走位分析

① class object(類(lèi)對(duì)象)/ metaclass(元類(lèi))
  • Object-C的對(duì)象其本質(zhì)就是結(jié)構(gòu)體,前面也分析了每一個(gè)對(duì)象都會(huì)有一個(gè)isa。同時(shí)類(lèi)的本質(zhì)也是一個(gè)結(jié)構(gòu)體,而且是繼承自objc_object的。
struct objc_object {private:isa_t isa;...};struct objc_class : objc_object {// Class ISA;Class superclass;// 方法緩存cache_t cache; // formerly cache pointer and vtable// 用于獲取具體的類(lèi)信息class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags...};
  • 在objc_class中也有isa:
struct objc_class : objc_object {isa_t isa;Class superclass;cache_t cache; // formerly cache pointer and vtableclass_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags...};
  • class_isMetaClass用于判斷Class對(duì)象是否為元類(lèi),object_getClass用于獲取對(duì)象的isa指針指向的對(duì)象。
OBJC_EXPORT BOOL class_isMetaClass(Class cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);OBJC_EXPORT Class object_getClass(id obj) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  • 我們知道:對(duì)象可以創(chuàng)建多個(gè),但是類(lèi)是否可以創(chuàng)建多個(gè)呢?其實(shí)答案是否定的,類(lèi)在內(nèi)存中只會(huì)存在一份。
Class class1 = [Boy class];Class class2 = [Boy alloc].class;Class class3 = object_getClass([Boy alloc]);Class class4 = [Boy alloc].class;NSLog(@"\n%p-\n%p-\n%p-\n%p",class1, class2, class3, class4);// 打印如下:0x10edbedc80x10edbedc80x10edbedc80x10edbedc8
  • 通過(guò) LLDB 調(diào)試打印,其實(shí)可以發(fā)現(xiàn):類(lèi)的內(nèi)存結(jié)構(gòu)里面的第一個(gè)結(jié)構(gòu)打印出來(lái)還是 Boy,那么是不是就意味著 對(duì)象 ->類(lèi)->類(lèi) 這樣的死循環(huán)呢?這里的第二個(gè)類(lèi)其實(shí)是 元類(lèi),是由系統(tǒng)創(chuàng)建的,這個(gè)元類(lèi)無(wú)法被我們實(shí)例化。

  • 一個(gè)實(shí)例對(duì)象通過(guò)class方法獲取的Class就是它的isa指針指向的類(lèi)對(duì)象,而類(lèi)對(duì)象不是元類(lèi),類(lèi)對(duì)象的isa指針指向的對(duì)象是元類(lèi)。關(guān)系如下:
② isa 走位
  • 官方的經(jīng)典 isa 走位圖:
    • 實(shí)例對(duì)象的isa指向的是類(lèi);
    • 類(lèi)的isa指向的元類(lèi);
    • 元類(lèi)指向根元類(lèi);
    • 根元類(lèi)指向自己;
    • NSObject的父類(lèi)是nil,根元類(lèi)的父類(lèi)是NSObject。

  • LLDB 調(diào)試打印:

六、對(duì)象的本質(zhì) isa

  • OC 對(duì)象的本質(zhì)就是一個(gè)結(jié)構(gòu)體,在 libObjc 源碼的 objc-private.h 源文件中可以看到:
struct objc_object {private:isa_t isa;public:// ISA() assumes this is NOT a tagged pointer objectClass ISA();// getIsa() allows this to be a tagged pointer objectClass getIsa();......}
  • 對(duì)于對(duì)象所屬的類(lèi)來(lái)說(shuō),也可以在 objc-runtime-new.h 源文件中找到(即 objc_class 內(nèi)存中第一個(gè)位置是 isa,第二個(gè)位置是 superclass):
struct objc_class : objc_object {// Class ISA;Class superclass;cache_t cache; // formerly cache pointer and vtableclass_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags......}
  • 對(duì)象在底層其實(shí)是一個(gè)結(jié)構(gòu)體 objc_object ,而Class 在底層也是一個(gè)結(jié)構(gòu)體 objc_class 。

總結(jié)

以上是生活随笔為你收集整理的iOS之深入解析对象isa的底层原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。