iOS底层探索类的本质

ISA走位

定义一个继承NSObject的类ABPerson,通过lldb命令查看其ISA的指向。

实例对象的ISA指向类对象

图片.png

  • p/x p打印实例对象p的地址
  • x/4gx 0x0000000100538ae0打印该地址的内存,输出4段,首段存储着ISA相关信息
  • p/x 0x011d800100008185 & 0x00007ffffffffff8ULL,通过约ISA_MASK(0x00007ffffffffff8ULL)的&操作,得到类对象ABPerson
  • po 0x0000000100008180打印类对象

结论:实例对象的ISA指向类对象

类对象的ISA指向其元类

由上图可知0x0000000100008180是类对象的地址,继续查看类对象的ISA的指向:
图片.png
能够看到得到了一个地址0x0000000100008158,打印也是ABPerson,这是就是元类,是由系统生成。类对象只有一个,可证明如下:

图片.png
类对象ABPerson的地址都是:0x1000081900x0000000100008158是元类地址。

结论:类对象的ISA指向其元类

元类的ISA指向根元类

由上图可知0x0000000100008158是类的元类地址,继续查看元类的ISA的指向:
图片.png
能够看到元类的ISA是指向NSObject,也就是根元类。
结论:元类的ISA是指向根元类

根元类的ISA指向其自身

由上图可知0x00007fff88967fe0是根元类NSObject的地址,继续查看根元类的ISA的指向:
图片.png
红框的地址都是相同的,都是其自身。
结论:根元类的ISA指向其自身

ISA走位图

isa.png

继承链

定义一个子类ABTeacher继承ABPerson
图片.png
class.png

图片.png

  • NSObject的元类、根元类、根根元类都是0x7fff88967fe0,是一个东西。
  • ABPersonABTeacher是父类,ABTeacher元类的父类地址是0x100008210ABPerson的元类地址也是0x100008210,所以ABTeacher元类的父类就ABPerson的元类。
  • 根类NSObject的父类为nil
  • 根元类的父类地址是0x7fff88968008就是NSObject

继承.png

最后附上苹果官方的的图
isa流程图.png

类的结构

objc源码中查看Class的定义:

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()
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享