iOS底層探索類的本質

ISA走位

定義一個繼承NSObject的類ABPerson,經過lldb命令查看其ISA的指向。git

實例對象的ISA指向類對象

圖片.png

  • p/x p打印實例對象p的地址
  • x/4gx 0x0000000100538ae0打印該地址的內存,輸出4段,首段存儲着ISA相關信息
  • p/x 0x011d800100008185 & 0x00007ffffffffff8ULL,經過約ISA_MASK(0x00007ffffffffff8ULL)的&操做,獲得類對象ABPerson
  • po 0x0000000100008180打印類對象

結論:實例對象的ISA指向類對象github

類對象的ISA指向其元類

由上圖可知0x0000000100008180是類對象的地址,繼續查看類對象的ISA的指向: 圖片.png 可以看到獲得了一個地址0x0000000100008158,打印也是ABPerson,這是就是元類,是由系統生成。類對象只有一個,可證實以下:markdown

圖片.png 類對象ABPerson的地址都是:0x1000081900x0000000100008158是元類地址。函數

結論:類對象的ISA指向其元類oop

元類的ISA指向根元類

由上圖可知0x0000000100008158是類的元類地址,繼續查看元類的ISA的指向: 圖片.png 可以看到元類的ISA是指向NSObject,也就是根元類。 結論:元類的ISA是指向根元類佈局

根元類的ISA指向其自身

由上圖可知0x00007fff88967fe0是根元類NSObject的地址,繼續查看根元類的ISA的指向: 圖片.png 紅框的地址都是相同的,都是其自身。 結論:根元類的ISA指向其自身ui

ISA走位圖

isa.png

繼承鏈

定義一個子類ABTeacher繼承ABPerson 圖片.png class.pngatom

圖片.png

  • NSObject的元類、根元類、根根元類都是0x7fff88967fe0,是一個東西。
  • ABPersonABTeacher是父類,ABTeacher元類的父類地址是0x100008210ABPerson的元類地址也是0x100008210,因此ABTeacher元類的父類就ABPerson的元類。
  • 根類NSObject的父類爲nil
  • 根元類的父類地址是0x7fff88968008就是NSObject

繼承.png

最後附上蘋果官方的的圖 isa流程圖.pngspa

類的結構

objc源碼中查看Class的定義:debug

typedef struct objc_class *Class;

objc_class的結構體定義:

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

省略部分代碼
   
複製代碼

類的結構體佈局大概以下:

class.png

  • ISAsuperclass都佔用一個結構體指針的大小8字節
  • cache16個字節,cache_t結構體:
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
    union {
    //聯合體互斥因此佔8
        struct {
            explicit_atomic<mask_t>    _maybeMask;  //uint32_t 4
#if __LP64__
            uint16_t                   _flags;  //uint16_t 2
#endif
            uint16_t                   _occupied; //uint16_t 2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8
    };
複製代碼

_bucketsAndMaybeMask8,當前union8,因此cache_t16

若是想訪問bits就須要內存平移8+8+16=32個字節。

獲取類的屬性

lldb調試:

調試工程下載

@interface LGPerson : NSObject
// isa
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;

- (void)saySomething;

@end
複製代碼

圖片.png

  • x/4gx LGPerson.class獲取類的首地址
  • p/x 0x1000083a0 + 0x20,首地址偏移32個字節,拿到bits
  • p (class_data_bits_t *)0x00000001000083c0,將地址強轉成class_data_bits_t類型
  • p $2->data(),調用class_data_bits_t中的data()函數
struct class_data_bits_t {
省略部分代碼
public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    省略部分代碼
 }
複製代碼
  • p $3->properties()獲取類的屬性
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 *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }
複製代碼
class property_array_t : 
    public list_array_tt<property_t, property_list_t, RawPtr>
{
    typedef list_array_tt<property_t, property_list_t, RawPtr> Super;

 public:
    property_array_t() : Super() { }
    property_array_t(property_list_t *l) : Super(l) { }
};
複製代碼
class list_array_tt {
    struct array_t {
        uint32_t count;
        Ptr<List> lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count*sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };
    省略部分代碼
 }
複製代碼

property_array_t繼承自list_array_tt,list_array_tt中有個結構體array_t,結構體中有變量lists

  • p $4.list拿到property_list_t首地址
  • p $5.ptr打印property_list_t首地址
  • p *$6取地址拿到property_list_t
  • p $7.get(0)讀取property_list_t中的成員變量

獲取類的實例方法

圖片.png

圖片.png

  • 前面相同的步驟這裏直接省略介紹
  • p $3->methods()獲取類的方法
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 *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }
複製代碼
class method_array_t : 
    public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>
{
    typedef list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> Super;

 public:
    method_array_t() : Super() { }
    method_array_t(method_list_t *l) : Super(l) { }

    const method_list_t_authed_ptr<method_list_t> *beginCategoryMethodLists() const {
        return beginLists();
    }
    
    const method_list_t_authed_ptr<method_list_t> *endCategoryMethodLists(Class cls) const;
};
複製代碼
class list_array_tt {
    struct array_t {
        uint32_t count;
        Ptr<List> lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count*sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };
    省略部分代碼
 }
複製代碼

method_array_t繼承自list_array_ttlist_array_tt中有個結構體array_t,結構體中有變量lists

  • p $4.list拿到method_list_t首地址
  • p $5.ptr打印method_list_t首地址
  • p *$6取地址拿到method_list_t
  • p $7.get(0).big()讀取 method_list_t中的實例方法
struct property_t {
    const char *name;
    const char *attributes;
};

複製代碼
struct method_t {
    static const uint32_t smallMethodListFlag = 0x80000000;

    method_t(const method_t &other) = delete;

    // The representation of a "big" method. This is the traditional
    // representation of three pointers storing the selector, types
    // and implementation.
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    省略部分代碼
  }
複製代碼

對比結構體property_tmethod_t可以看到,獲取屬性可直接$7.get(0)拿到結構體,而method_t裏面還有一個結構體big,具體方法是放在big裏面,因此要調用big()

獲取類的成員變量

修改LGPerson代碼,添加一個成員變量,一個類方法

@interface LGPerson : NSObject
{
    //成員變量
    NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;

- (void)saySomething;
//類方法
+ (void)doSomething;
@end
複製代碼
@implementation LGPerson
- (void)saySomething{
    NSLog(@"%s",__func__);
}
+ (void)doSomething{
    NSLog(@"%s",__func__);
}
@end

複製代碼

圖片.png

  • 前面相同的步驟這裏直接省略介紹

- $3->ro(),獲取class_ro_t地址

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_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }
複製代碼
  • p *$4取地址,得到class_ro_t
  • p $5.ivars獲取class_ro_t的成員變量列表ivar_list_t的首地址
struct class_ro_t {
    省略部分代碼
    const ivar_list_t * ivars;
    省略部分代碼
 }
複製代碼
  • p *$6取地址得到成員變量列表ivar_list_t
  • p $7.get(0)打印成員變量

獲取類方法

圖片.png

  • x/4gx LGPerson.class,打印LGPerson的內存,獲得前8個字節0x00000001000083c0
  • p/x 0x00000001000083c0 & 0x00007ffffffffff8ULL,得到元類地址
  • 後面步驟和得到實例方法一致(略)

總結

  • ISA走位:實例對象的ISA指向類對象,類對象的ISA指向其元類,元類的ISA指向根元類,根元類的ISA指向其自身
  • 繼承鏈:
    • 子類繼承自父類,父類繼承自根類,根類繼承nil
    • 子元類繼承自父元類,父元類繼承自根元類,根元類繼承自根類
  • 類的結構:ISAsuperclasscachebits
  • 獲取類的屬性:獲取類的首地址->偏移32位拿到class_data_bits_t->data()->properties()->property_list_t->get()
  • 獲取類的實例方法:獲取類的首地址->偏移32``位拿到class_data_bits_t->data()->methods()->method_list_t->get().big()
  • 獲取類的成員變量:獲取類的首地址->偏移32位拿到class_data_bits_t->data()->ro()->ivar_list_t->get()
  • 獲取類方法:獲取元類地址->偏移32``位拿到class_data_bits_t->data()->methods()->method_list_t->get().big()
相關文章
相關標籤/搜索