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

前言

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

一.了解clang

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

  • Clang是?個C語?、C++、Objective-C語?的輕量級編譯器
  • Clang將?持其普通lambda表達式、返回類型的簡化處理以及更好的處理constexpr關鍵字
  • Clang是?個由Apple主導編寫,基于LLVM的C/C++/Objective-C編譯器
  • 2013年4?,Clang已經(jīng)全??持C++11標準,并開始實現(xiàn)C++1y特性(也就是C++14,這是C++的下?個?更新版本)
  • Clang是?個C++編寫、基于LLVM、發(fā)布于LLVM BSD許可證下的C/C++/Objective-C/Objective-C++編譯器。

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

  1. 直接命令行編譯 :
//  把?標?件編譯成c+
clang -rewrite-objc main.m -o main.cpp 
  1. xcode安裝的時候順帶安裝了xcrun命令,xcrun命令在clang的基礎上進?了?些封裝,要更好??些:
//  模擬器
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 
  1. 成功生成如下文件:


三.分析c++文件

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

LGPerson

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

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


isa
objc

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

  • id class = [class new] 為什么我們id 類型可以獲取所有的屬性類型而且不需要加,因為他的底層就是id
@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é):對象本質(zhì)就是一個結(jié)構(gòu)體,屬性成員變量實現(xiàn)了 get 和 set 方法.
  • 流程分析 :LGPerson —> 找到LGPerson_IMPL —> NSObject_IMPL -—> Class

四.isa分析

struct NSObject_IMPL {
    Class isa;
};

在上面的代碼里,我們可以看到NSObject里面只有一個成員變量,那就是Class類型的isa。那么這isa是什么呢,我們就這個問題,繼續(xù)探索下去,首先我們先看看他的類型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實際上就是一個objc_class *類型的結(jié)構(gòu)體指針。

接下來我們來看看isa,在alloc流程的最后一步,就是通過initIsa方法將我們申請的內(nèi)存地址和我們的Class綁定起來。

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

在這些代碼中間,有個非常重要的東西,就是isa_t,我們再來看看它到底是什么:

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)合體

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

// 結(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)點:海納百川,有容乃大。只要你來,我都給你存下來
    缺點:內(nèi)存空間的分配是粗放的,不管你用不用全都給你分配好位置

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容