iOS 對象相關(guān)知識

對象的分類

  1. 實(shí)例對象;
  2. 類對象;
  3. 元類對象;

1. 實(shí)例對象(instance)

每次alloc 的都是一個新的對象, 地址也不相同;

每個實(shí)例對象中包含的信息有

  • isa 指針(指向類對象)
  • 其他的成員變量(成員變量的值信息之類, 因?yàn)槊總€示例對象的同一個成員變量值可能不同)
  • 內(nèi)存中存放在堆區(qū);
///實(shí)例對象
Model *obj1 = [[Model alloc] init];
Model *obj2 = [[Model alloc] init];
Model *obj3 = [[Model alloc] init];
NSLog(@"實(shí)例對象: obj1: %p,  obj2: %p,  obj3:  %p",  obj1, obj2, obj3);
    
2020-06-18 10:16:32.152935+0800 Instance對象的本質(zhì)[3695:54819] 實(shí)例對象: obj1: 0x600001740ed0,  obj2: 0x600001740ea0,  obj3:  0x600001740eb0

通過源碼驗(yàn)證
///Model為繼承自 NSObject 的類
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Model : NSObject {
   @public
    int _age;
    double _height;
}
@property (nonatomic, assign) int count;
@end
NS_ASSUME_NONNULL_END
///.m 實(shí)現(xiàn)
#import "Model.h"
@implementation Model

@end
通過指令將Model.m轉(zhuǎn)換為C++文件后可以得到;
Model 的實(shí)例對象存儲結(jié)構(gòu)
struct Model_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    double _height;
    int _count;
};
==>
struct NSObject_IMPL {
    Class isa;
};

===>所以 Model的結(jié)構(gòu)即:

struct Model_IMPL {
    Class  isa;///isa 指針
    int _age;///成員變量
    double _height;///成員變量
    int _count;///成員變量(類中的屬性=成員變量+setter+getter)

};

2. 類對象(Class)

一個類的類對象無論何種方式獲取只有一個, 地址始終是同一個;

每個類對象中包含的信息有

  • isa 指針(指向元類對象)
  • superclass 指針(指向父類的類對象, 基類的superclass指向nil)
  • 類的屬性方法信息@property, 類的實(shí)例方法(-號方法)
  • 類的協(xié)議信息, 類的成員變量信息ivar (注意: 此處的成員變量信息不是值, 而是成員變量類型/名字之類的通用描述信息)
  • 內(nèi)存中存放在代碼段;
    ///類對象
    Class objClass1 = [obj1 class];
    Class objClass2 = [obj2 class];
    Class objClass3 = [NSObject class];
    Class objClass4 = [[[[[NSObject class] class] class] class] class];
    NSLog(@"類對象: objClass1: %p,  objClass2: %p,  objClass3:  %p,  objClass4:  %p",  objClass1, objClass2, objClass3, objClass4);

    2020-06-18 10:16:32.153058+0800 Instance對象的本質(zhì)[3695:54819] 類對象: objClass1: 0x7fff89c1ed00,  objClass2: 0x7fff89c1ed00,  objClass3:  0x7fff89c1ed00,  objClass4:  0x7fff89c1ed00


    ///通過object_getClass獲取對象
    //入?yún)?shí)例對象獲得類對象
    //入?yún)㈩悓ο螳@取元類對象  
    //源碼可以得到原因,  因?yàn)槭峭ㄟ^ isa 指針去尋找的;
    Class  metaClass11 =  object_getClass(obj1);
    Class  metaClass22 =  object_getClass(obj2);
    Class  metaClass33 =  object_getClass(obj3);
    NSLog(@"通過實(shí)例對象獲取元類對象: metaClass11: %p,  metaClass22: %p,  metaClass33:  %p",  metaClass11, metaClass22, metaClass33);

2020-06-18 10:16:32.153165+0800 Instance對象的本質(zhì)[3695:54819] 通過實(shí)例對象獲取元類對象: metaClass11: 0x7fff89c1ed00,  metaClass22: 0x7fff89c1ed00,  metaClass33:  0x7fff89c1ed00

objc 源碼看類對象的結(jié)構(gòu)
struct objc_object {
private:
    isa_t isa;
...
}
===>

struct objc_class : objc_object {
    ///首先存放的是 isa 指針
    // Class ISA;
    ///存放 superclass 指針
    Class superclass;
    ///方法緩存
    cache_t cache;             // formerly cache pointer and vtable
    /*
    具體的類信息, 看注釋,這個東西的得來是靠class_rw_t 加上自定義的 rr/alloc得來的;
      class_rw_t: 可以理解為 class_readwrite_table;
      class_ro_t: 可以理解為 class_readonly_table;
  */
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        ASSERT(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        ASSERT(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }
...
}
===>


struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;

...

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>();
        } else {
            return extAlloc(v.get<const class_ro_t *>());
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }
    /*
      存儲著一些信息
    */
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_t *>();
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }
    ///方法列表
    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
        }
    }
  ///屬性列表
    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
        }
    }
    ///協(xié)議列表
    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
};
===>
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    ///成員變量
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
...
}
總結(jié)起來類對象中的存儲結(jié)構(gòu)大致為
struct objc_class : objc_object {
    ///首先存放的是 isa 指針
    Class isa;
    ///存放 superclass 指針
    Class superclass;
    ///方法緩存
    cache_t cache; 
    ///方法列表
    method_array_t methods() 
    ///屬性列表
    property_array_t properties() 
    ///協(xié)議列表
    protocol_array_t protocols()
     ///成員變量
    const ivar_list_t * ivars;
    ....
}

===>
元類對象的存儲結(jié)構(gòu)跟類的存儲結(jié)構(gòu)一樣, 不過有一些部分是 null;

3. 元類對象(Meta-Class)

通過object_getClass方法, 入?yún)㈩悓ο螳@取到的是元類對象; 每個類的元類對象內(nèi)存中只有一個, 地址始終同一份; meta-classclass的內(nèi)存結(jié)構(gòu)一樣;
每個元類對象中包含的信息有

  • isa 指針(指向基類元類對象, NSObject元類對象)
  • superclass 指針(指向父類的元類對象)特殊:基類的元類對象指向基類的類對象
  • 類的方法(+號方法)
    ///通過類對象獲取元類對象
    Class  metaClass1 =  object_getClass(objClass1);
    Class  metaClass2 =  object_getClass(objClass2);
    Class  metaClass3 =  object_getClass(objClass3);
    Class  metaClass4 =  object_getClass(objClass4);
    NSLog(@"通過類對象獲取元類對象: metaClass1: %p,  metaClass2: %p,  metaClass3:  %p,  metaClass4:  %p",  metaClass1, metaClass2, metaClass3, metaClass4);

    
    
    ///是否是元類對象
    BOOL  ismeta1 = class_isMetaClass(objClass1);
    BOOL  ismeta2 = class_isMetaClass(objClass2);
    BOOL  ismeta3 = class_isMetaClass(metaClass1);
    BOOL  ismeta4 = class_isMetaClass(metaClass2);
    NSLog(@"是否是元類: objClass1: %ld,  objClass2: %ld,  metaClass1: %ld,  metaClass2: %ld",  (long)ismeta1, (long)ismeta2, (long)ismeta3, (long)ismeta4);


2020-06-18 10:16:32.153275+0800 Instance對象的本質(zhì)[3695:54819] 通過類對象獲取元類對象: metaClass1: 0x7fff89c1ecd8,  metaClass2: 0x7fff89c1ecd8,  metaClass3:  0x7fff89c1ecd8,  metaClass4:  0x7fff89c1ecd8
2020-06-18 10:16:32.153358+0800 Instance對象的本質(zhì)[3695:54819] 是否是元類: objClass1: 0,  objClass2: 0,  metaClass1: 1,  metaClass2: 1
對象中 isa 的指向

4. Class object_getClass(id obj)方法

Class object_getClass(id obj)方法;
通過 objc4源碼可以查到其實(shí)現(xiàn), 是通過入?yún)⒌?isa 指針去獲取返回內(nèi)容, 對象之間的 isa 指向關(guān)系如上;

  1. 實(shí)例對象的 isa 指向類對象, 所以入?yún)?shí)例對象, 返回類對象;
  2. 類對象的 isa 指向元類對象, 所以入?yún)㈩悓ο? 返回元類對象;
/***********************************************************************
* object_getClass.
* Locking: None. If you add locking, tell gdb (rdar://7516456).
**********************************************************************/
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

object_getClass源碼

5. objc_getClass(const char * _Nonnull name)方法

objc_getClass(const char * _Nonnull name)入?yún)⒁粋€字符串嘗試返回一個類; 如果可以映射到則返回類, 如果不能則返回 nil;

/***********************************************************************
* objc_getClass.  Return the id of the named class.  If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of 
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
Class objc_getClass(const char *aClassName)
{
    if (!aClassName) return Nil;

    // NO unconnected, YES class handler
    return look_up_class(aClassName, NO, YES);
}

6. 調(diào)用方法的流程(簡單流程)

子類調(diào)用父類實(shí)例/類方法的流程;
類對象的superclass指針指向父類的類對象;
元類對象的superclass指針指向父類的元類對象;

  1. 調(diào)用方法的代碼編譯后變成如下

注意:發(fā)送的消息只是確定方法名字, 并不指定是"+"號方法或者"-"號方法;

((void (*)(id, SEL))(void *)objc_msgSend)((id)sonModel, sel_registerName("ModelInstanceMethod"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SonModel"), sel_registerName("ModelClassMethod"));

不考慮[動態(tài)解析][消息轉(zhuǎn)發(fā)]情況下調(diào)用方法流程為; 完整的流程在objc_msgSend();

  • [sonModel ModelInstanceMethod]; 調(diào)用實(shí)例方法流程為, 首先查找緩存, 如果沒有則通過實(shí)例對象的isa指針查找到對應(yīng)的類對象, 然后查找相應(yīng)的實(shí)例方法, 如果能找到則調(diào)用, 如果不能找到, 則通過類對象的superclass指針查找父類的類對象, 然后查找相應(yīng)的實(shí)例方法, 找到基類的類對象仍然沒有找到; 如果沒實(shí)現(xiàn)動態(tài)解析消息轉(zhuǎn)發(fā) 則拋出錯誤未找到實(shí)例方法的實(shí)現(xiàn); xcode中代碼: unrecognized selector sent to instance;
  • [SonModel ModelClassMethod]; 調(diào)用類方法流程為, 首先查找緩存,如果沒有, 通過類對象的isa指針查找到對應(yīng)的元類對象, 然后查找相應(yīng)的類方法, 如果能找到則調(diào)用, 如果不能找到, 則通過元類對象的superclass指針查找父類的元類類對象, 然后查找相應(yīng)的類方法, 找到基類的元類對象仍然沒有找到, 則去基類的類對象中查找, 如果仍然沒有; 如果沒實(shí)現(xiàn)動態(tài)解析消息轉(zhuǎn)發(fā) 則 拋出錯誤未找到類方法的實(shí)現(xiàn)(默認(rèn)認(rèn)為沒有同名實(shí)例方法); xcode 中代碼: unrecognized selector sent to class;
    特殊情況詳見 7.isa 和 superclass 的指向
SonModel *sonModel = [[SonModel alloc] init];
[sonModel ModelInstanceMethod];
[SonModel ModelClassMethod];
///父類
@interface Model : NSObject {
   @public
    int _age;
    double _height;
}
- (void)ModelInstanceMethod;
+ (void)ModelClassMethod;
@end

///子類
@interface SonModel : Model {
    int _count;
}
- (void)SonModelInstanceMethod;
+ (void)SonModelClassMethod;
@end

7. isa 和 superclass 的指向

isa 和 superclass 的指向 如下

isa和 superclass 的指向

接著知識點(diǎn)6解釋下那種特殊情況: 如果調(diào)用類方法在基類的元類對象中仍然沒有找到, 則會基類的元類對象的superclass指針去基類的類對象中查找同名的實(shí)例方法, 如果有同名的實(shí)例方法則使用; 如果沒有則拋出錯誤;
具體測試代碼如下
NSObject添加Catogary如下

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Method)
+ (void)test;
@end
NS_ASSUME_NONNULL_END

#import "NSObject+Method.h"
@implementation NSObject (Method)
- (void)test {
    NSLog(@"NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為%p", self);
}
@end


#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Model : NSObject
+ (void)test;
@end
NS_ASSUME_NONNULL_END


#import "Model.h"
@implementation Model
@end

運(yùn)行結(jié)果如下, 由此可以驗(yàn)證如上結(jié)論;

NSLog(@"NSOject類對象地址: %p,  Model類對象地址: %p", [NSObject class], [Model class]);
[NSObject test];
[Model test];

2020-06-18 21:02:59.282460+0800 Instance對象的本質(zhì)[2833:21502] NSOject類對象地址: 0x101ec5f38,  Model類對象地址: 0x100e2b640
2020-06-18 21:02:59.282578+0800 Instance對象的本質(zhì)[2833:21502] NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為0x101ec5f38
2020-06-18 21:02:59.282664+0800 Instance對象的本質(zhì)[2833:21502] NSObject實(shí)現(xiàn)-方法 調(diào)用者的地址為0x100e2b640

補(bǔ)充:
  • superclass指針的地址直接就是相應(yīng)父類對象的地址;
  • isa的值并不是直接指向類/元類對象的地址, 從64位系統(tǒng)開始isa的值進(jìn)行一次位運(yùn)算后才能得出相應(yīng)的類/元類對象的地址;
    實(shí)例對象isa & ISA_MASK = 類對象地址;
    類對象isa & ISA_MASK = 元類對象地址;
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL//arm64架構(gòu)使用這個值:  0x0000000ffffffff8
#   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//x86_64架構(gòu)使用這個值:  0x00007ffffffffff8
#   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

通過指令將OC文件轉(zhuǎn)換為C++文件
指令: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件.m -o 文件-arm64.cpp
如果需要鏈接其他框架, 使用-framework參數(shù); 例: -framework UIKit


參考文章和下載鏈接
Apple 一些源碼的下載地址


有理解錯誤地方請不吝賜教

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末燥筷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子戏罢,更是在濱河造成了極大的恐慌惭笑,老刑警劉巖偏陪,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡垃喊,警方通過查閱死者的電腦和手機(jī)嚼松,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門嫡良,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人献酗,你說我怎么就攤上這事寝受。” “怎么了罕偎?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵很澄,是天一觀的道長。 經(jīng)常有香客問我颜及,道長甩苛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任俏站,我火速辦了婚禮讯蒲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肄扎。我一直安慰自己墨林,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布反浓。 她就那樣靜靜地躺著萌丈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雷则。 梳的紋絲不亂的頭發(fā)上辆雾,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機(jī)與錄音月劈,去河邊找鬼度迂。 笑死,一個胖子當(dāng)著我的面吹牛猜揪,可吹牛的內(nèi)容都是我干的惭墓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼而姐,長吁一口氣:“原來是場噩夢啊……” “哼腊凶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钧萍,失蹤者是張志新(化名)和其女友劉穎褐缠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體风瘦,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡队魏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了万搔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胡桨。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞬雹,靈堂內(nèi)的尸體忽然破棺而出昧谊,到底是詐尸還是另有隱情,我是刑警寧澤酗捌,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布揽浙,位于F島的核電站,受9級特大地震影響意敛,放射性物質(zhì)發(fā)生泄漏馅巷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一草姻、第九天 我趴在偏房一處隱蔽的房頂上張望钓猬。 院中可真熱鬧,春花似錦撩独、人聲如沸敞曹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澳迫。三九已至,卻和暖如春剧劝,著一層夾襖步出監(jiān)牢的瞬間橄登,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工讥此, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拢锹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓萄喳,卻偏偏與公主長得像卒稳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子他巨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359