經過源碼分析isa

通篇以一下代碼爲例objc4-781源碼中驗證對應方法ios

#import <Foundation/Foundation.h>
#import <objc/runtime.h>


@interface TDPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation TDPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

複製代碼
  • clang

    • 概念

    Clang是⼀個C語⾔、C++、Objective-C語⾔的輕量級編譯器。源代碼發佈於BSD協議下。c++

    Clang將⽀持其普通lambda表達式、返回類型的簡化處理以及更好的處理constexpr關鍵字。
    **Clang是⼀個由Apple主導編寫,基於LLVM的C/C++/Objective-C編譯器 **
    2013年4⽉,Clang已經全⾯⽀持C++11標準,並開始實現C++1y特性(也就是C++14,這是 C++的下⼀個⼩更新版本)Clang將⽀持其普通lambda表達式、返回類型的簡化處理以及更 好的處理constexpr關鍵字。 [2] Clang是⼀個C++編寫、基於LLVM、發佈於LLVM BSD許可證下的C/C++/Objective-C/ Objective-C++編譯器。它與GNU C語⾔規範⼏乎徹底兼容(固然,也有部分不兼容的內容, 包括編譯命令選項也會有點差別),並在此基礎上增長了額外的語法特性,⽐如C函數重載 (經過_ attribute_((overloadable))來修飾函數),其⽬標(之⼀)就是超越GCC。算法

    • 將oc文件編譯成c++文件的編譯指令
      1. clang -rewrite-objc main.m -o main.cpp
      2. clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk main.m
      3. xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m -o main-x86_64.cpp(模擬器)
      4. xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp(手機)
  • 對象的本質

    1. 將.m文件編譯成.cpp文件並全局搜索TDPerson可獲得

    發現對象在底層會被編譯成結構體 2. NSObject_IMPL探索
    從上述編譯後的代碼能夠發現結構體繼承NSObject_IMPL(這裏的繼承是僞繼承,這種寫法能夠達到繼承的效果,TDPerson_IMPL結構體能夠擁有NSObject_IMPL的全部屬性)NSObject_IMPL裏面又是什麼呢繼續在編譯後的文件中找 因此TDPerson_IMPL中的第一個屬性 NSObject_IVARS 其實就是NSObject中的isamarkdown

  • isa

    經過上述論證發現對象的本質就是結構體,而且該結構的第一個屬性是isa,isa又是什麼架構

    1. 根據源碼查找isa

    根據alloc & init & new 源碼分析文章中看alloc過程會發現initIsa的方法發現isa,是一個isa_t類型,繼續跟源碼看一下isa_t又是什麼發現是一個聯合體位域,因此isa其實就是一個聯合體位域 2. isa爲何是一個聯合體位域 * 聯合體屬性公用一片內存,成員之間是互斥的,這裏有兩個屬性cls、bits,這樣寫增長了isa_t的靈活性,能夠經過cls初始化isa,同事也能夠經過bits初始化isa * 位域:咱們知道主要是用來節省空間的,就是有些類信息其實不須要那麼多位數去存儲可能只須要一位就夠了,使用位域咱們就能夠節省很多空間,下圖是ISA_BITFIELD屬性佔用內存狀況: 能夠看到不少屬性只須要1位就夠了 3. isa中的屬性分別表明什麼 * 屬性含義 * 屬性內存佔用狀況圖解 4. 透過源碼查看isa初始化流程(initIsa)
    經過alloc & init & new 源碼分析文章能夠知道alloc方法會走到一個加initIsa的方法 5. 經過源碼驗證isa的內存分配狀況 1. initIsa源碼 ``` inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { ASSERT(!isTaggedPointer());app

    //從這裏就能夠驗證isa爲何用聯合體
        //若是僅僅就是一個存isa指針咱們只須要經過賦值cls初始化就好
        //若是含有一些類信息、引用計數等,就須要賦值bits來初始化isa
        if (!nonpointer) {
            //若是是純指針,直接設置到cls
            isa = isa_t((uintptr_t)cls);
        } else {
            ASSERT(!DisableNonpointerIsa);
            ASSERT(!cls->instancesRequireRawIsa());
            //初始化isa
            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;
        }
      }
    
     ```
     2. **isa_t newisa(0);**以後打印看結果![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0482e167bc36440a9508e3da919df4fd~tplv-k3u1fbpfcp-zoom-1.image)
     3. **newisa.bits = ISA_MAGIC_VALUE**以後再看結果
     4. 對比第二三步得出結論  
        1. **ISA_MAGIC_VALUE**是就是給nonpointer以及magic賦值的
        2. 怎麼賦值的爲何nonpointer是1,magic是59  
        首先透過源碼仔細看一下**ISA_MAGIC_VALUE**究竟是什麼![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c685d55cff28435998cb49d933e40b4c~tplv-k3u1fbpfcp-zoom-1.image)發現就是一個宏,而後這個宏是一個16進制的值**0x001d800000000001**,下一步再將這個16進制的值轉換成二進制![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a606bac1df454708bb22c2231ef5b831~tplv-k3u1fbpfcp-zoom-1.image)從上面isa的屬性內存分佈圖不難看出nonpointer存儲在第0位置的因此是1毋庸置疑,而後咱們項目是跑在mac上的不是真機,因此**CPU架構師x86_64**,因此呢shiftcls就佔用了44位,加上前面三位(nonpointer、has_assoc、has_cxx_dtor各佔用1位)而後又是從0位置開始得出magic從47位開始讀,而後magic又是佔用6位因此獲得magic=111011,而後再轉換成10進制獲得是59
        2. 驗證了聯合體的特性bits、cls互斥公用一段內存上圖中8303511812964353轉換成16進制其實就是0x001d800000000001,因此這兩個屬性值同樣![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/975e363198f048749f1d2767b9302c2b~tplv-k3u1fbpfcp-zoom-1.image)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/60e08999e7694ac6aa47937ee7d5d8ce~tplv-k3u1fbpfcp-zoom-1.image)
     5. 細節分析**newisa.shiftcls = (uintptr_t)cls >> 3;**  
     應爲賦值會從第0位置開始讀取數據,然而cls中的shiftcls信息是從第三位開始存儲的,因此要右移三位,還一個就是shiftcls賦值爲何要強轉:由於內存的存儲不能存儲字符串,機器碼只能識別 0 、1這兩種數字,因此須要將其轉換爲uintptr_t數據類型,這樣shiftcls中存儲的類信息才能被機器碼理解, 其中uintptr_t是long
    複製代碼
    1. isa與類的關聯

    上面的代碼繼續往下執行**newisa.shiftcls = (uintptr_t)cls >> 3;**以後在打印newisa看結果 發現此時cls就是TDPerson,這個時候再將newisa賦值給isa那麼isa就和類關聯起來了iphone

  • 經過isa獲取類信息

    1. 經過x/4gx 查看內存分配狀況

    應爲isa是全部對象的第一個參數,因此就能夠獲得isa的指針地址0x001d8001000020e9 2. 第一種方法經過isa & ISA_MASK獲得類其實& ISA_MASK就是一個小算法的到shiftcls值,應爲isa裏面只有shiftcls存儲的纔是類的指針 3. 經過位運算
    想要獲取類信息其實就是獲取isa裏面的shiftcls信息,因此要想獲取到shiftcls信息確定要將其餘位置的信息剔除掉,上文說過shiftcls無論在arm_64仍是x86_64的架構中都是衝第3各位值存儲的只不過shiftcls佔用的值不同,因此在arm_64位的系統中須要剔除前三位和後面28位的信息,而在x86_64的架構中須要剔除前三位和後面17位的信息,最重要的已不是要將shiftcls相對位置還原到初始的位置,而後在打印大概流程以下 4. 經過object_getClass獲取
    其實object_getClass源碼實現就是isa & ISA_MASK 函數

相關文章
相關標籤/搜索