iOS4 类结构分析

485 阅读5分钟

一,内存偏移

1,普通指针研究

    int a = 10;
    int b = 10;
    int *a_p = &a;
    int *b_p = &b;
    NSLog(@"%d -- %p -- %p",a,&a,&a_p);
    NSLog(@"%d -- %p -- %p",b,&b,&b_p);
    
//输出结果
2021-07-26 16:15:17.348698+0800 内存偏移[7982:190211] 10 -- 0x7ffee841a03c -- 0x7ffee841a030
2021-07-26 16:15:17.348855+0800 内存偏移[7982:190211] 10 -- 0x7ffee841a038 -- 0x7ffee841a028

  • a和b的值都是10 但是a和b地址不一样,整型赋值是值拷贝a和b属于两份数据
  • a地址是0x7ffee841a03c,b地址是0x7ffee841a038偏移量是4, 因为a,b是int类型
  • a_p地址是0x7ffee841a030,b_p地址是0x7ffee841a028偏移量是8,因为a_p,b_p是(int*)类型
  • 地址大小比较:a > b > a_p > b_p,由于是局部变量,他们都存放在栈区

2,对象指针研究

    NBPerson *p1 = [NBPerson alloc];
    NBPerson *p2 = [NBPerson alloc];
    NBPerson *p3 = p2;
    
    NSLog(@"---%@----%p",p1,&p1);
    NSLog(@"---%@----%p",p2,&p2);
    NSLog(@"---%@----%p",p3,&p3);
    
//输出结果   
2021-07-26 18:04:00.553396+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2e0>----0x7ffee47bb038
2021-07-26 18:04:00.553507+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2f0>----0x7ffee47bb030
2021-07-26 18:04:00.553599+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2f0>----0x7ffee47bb028
    
  • alloc 开辟的内存在堆区局部变量开辟的内存在栈区
  • 堆区地址 --> 地址,栈区地址 --> 地址
  • p3和p2存的都是第二次alloc的地址属于同一份数据

3,数组指针研究

        int c[4] = {1,2,3,4};
        int *d   = c;
        NSLog(@"%p - %p - %p ",&c,&c[0],&c[1]);
        NSLog(@"%p - %p - %p ",d,d+1,d+2);

        for (int i = 0; i<4; i++) {
            //(d+i) 取地址里面的值
            int value =  *(d+i); 
            NSLog(@"value = %d",value);
        }
        
//输出结果
2021-07-26 18:17:57.673897+0800 内存偏移[10303:272382] 0x7ffeef40c040 - 0x7ffeef40c040 - 0x7ffeef40c044
2021-07-26 18:17:57.674018+0800 内存偏移[10303:272382] 0x7ffeef40c040 - 0x7ffeef40c044 - 0x7ffeef40c048
2021-07-26 18:17:57.674109+0800 内存偏移[10303:272382] value = 1
2021-07-26 18:17:57.674187+0800 内存偏移[10303:272382] value = 2
2021-07-26 18:17:57.674255+0800 内存偏移[10303:272382] value = 3
2021-07-26 18:17:57.674330+0800 内存偏移[10303:272382] value = 4

//c的地址为7ffeef40c040
//c的四个元素分别为7ffeef40c040,7ffeef40c044,7ffeef40c048,7ffeef40c04c
  • 数组的地址就是数组元素中的首地址,即&c&c[0]都是首地址
  • 数组中每个元素之间的地址间隔,根据当前元素的数据类型决定的
  • 数组的元素地址可以通过首地址+n*类型大小方式,这种方式是数组中的元素类型必须相同。
  • 数组元素不相同用首地址+偏移量方式,根据当前变量的偏移值(需要前面类型大小相加)

二,isa走位

  • 通过之前的文章可以知道对象本质是结构体,结构体的第一个成员变量就是isa。而且isa就指向当前的类,苹果为什么要这么做呢?下面我们来探究一下

1,苹果官方isa走位图

25800336-68ce3f460035fedf.png

  • 从图中我们大概能看出一些规律
  1. 非根类(NSObject)对象的isa指向类,指isa向元类,元类isa指向根元类,根元类isa指向自己
  2. 根类(NSObject)对象的isa指向根类,根类isa指向根元类,根元类isa指向自己
  3. 非根类(NSObject)的父类的元类就是元类的父类

2,类对象内存个数

    Class class1 = [NBPerson class];
    Class class2 = [NBPerson alloc].class;
    Class class3 = object_getClass([NBPerson alloc]);
    Class class4 = [NBPerson alloc].class;
    NSLog(@"\n-%p-\n-%p-\n-%p-\n-%p-",class1,class2,class3,class4);
    
//输出结果
2021-07-27 10:19:47.940292+0800 内存偏移[21711:526731] 
-0x1021b5520-
-0x1021b5520-
-0x1021b5520-
-0x1021b5520-

源码分析:类对象的地址都是一样的,内存中每一个类只有一块内存,和普通的对象内存则不是同一个

  • 上篇文章拿到类的信息有三种,用掩码来获取是比较快的而且直接的。

  • objc4的源码可以拿到:

    x86_64:define ISA_MASK 0x00007ffffffffff8ULL
    arm64:define ISA_MASK 0x0000000ffffffff8ULL
    arm64(simulators):define ISA_MASK 0x007ffffffffffff8ULL

3,元类探究

    NBPerson *p1 = [[NBPerson alloc]init];
    NSLog(@"%@",p1);
  • 断点调试
2021-07-27 10:29:03.000337+0800 内存偏移[21861:534807] <NBPerson: 0x6000009e0230>
(lldb) p/x p1
(NBPerson *) $0 = 0x00006000009e0230
(lldb) x/4gx 0x00006000009e0230//对象地址
0x6000009e0230: 0x000000010c315520 0x0000000000000000
0x6000009e0240: 0x00001759aa2a0240 0x0000600000bc0026

//通过对象的isa找到类地址
(lldb) p/x 0x000000010c315520 & 0x007ffffffffffff8
(long) $1 = 0x000000010c315520
(lldb) po 0x000000010c315520
NBPerson

//通过类的isa找到元类地址
(lldb) x/4gx 0x000000010c315520
0x10c315520: 0x000000010c3154f8 0x00007fff86d50660
0x10c315530: 0x00006000037ed200 0x000580100000000f
(lldb) p/x 0x000000010c3154f8 & 0x007ffffffffffff8
(long) $9 = 0x000000010c3154f8
(lldb) po 0x000000010c3154f8
NBPerson

  • 断点流程总结
  1. 通过对象p1的isa 0x000000010c315520找到 类的地址0x000000010c315520
  2. 通过类NBPerson的isa 0x000000010c3154f8找到元类的地址0x000000010c3154f8

4,根元类引出

  • 继续上一步断点
//元类的isa找到根元类
(lldb) x/4gx 0x000000010c3154f8
0x10c3154f8: 0x00007fff86d50638 0x00007fff86d50638
0x10c315508: 0x00006000025e0700 0x0004c03100000007
(lldb) p/x 0x00007fff86d50638 & 0x007ffffffffffff8
(long) $11 = 0x00007fff86d50638
(lldb) po 0x00007fff86d50638
NSObject

//根元类的isa找到根根元类(其实就是自己)
(lldb) x/4gx 0x00007fff86d50638
0x7fff86d50638: 0x00007fff86d50638 0x00007fff86d50660
0x7fff86d50648: 0x00006000037e8400 0x0009c0310000000f
(lldb) p/x 0x00007fff86d50638 & 0x007ffffffffffff8
(long) $13 = 0x00007fff86d50638
(lldb) po 0x00007fff86d50638
NSObject
  • 断点流程总结
  1. 元类的isa指向根元类
  2. 根元类的isa指向自己

元类和根元类的探究刚好和isa走位图中 objc(对象) --> class(类) --> metaClass(元类) --> rootMetaClass(根元类) --> rootMetaClass(根元类自己)

5,类继承

    Class tMetaClass = object_getClass(NBTeacher.class);//NBTeacher的元类
    Class tMetaSuperClass = class_getSuperclass(tMetaClass);//NBTeacher的元类的父类
    
    Class pMetaClass = object_getClass(NBPerson.class); //NBPerson的元类
    Class pMeatSuperClass = class_getSuperclass(pMetaClass);//NBPerson的元类的父类
   
    Class nMetaClass = object_getClass(NSObject.class);//NSObject的元类
    Class nSuperClass = class_getSuperclass(NSObject.class);//NSObject的父类
    Class nMetaSuperClass = class_getSuperclass(nMetaClass);//NSObject的元类的父类
    
    NSLog(@"NBTeacher-%p",NBTeacher.class);
    NSLog(@"NBPerson-%p",NBPerson.class);
    NSLog(@"NSObject-%p",NSObject.class);
 
    NSLog(@"%@ - %p - %@ - %p",tMetaClass,tMetaClass,tMetaSuperClass,tMetaSuperClass);
    NSLog(@"%@ - %p - %@ - %p",pMetaClass,pMetaClass,pMeatSuperClass,pMeatSuperClass);
    NSLog(@"%@ - %p - %@ - %p",nMetaClass,nMetaClass,nMetaSuperClass,nMetaSuperClass);
    NSLog(@"%@ - %p",nSuperClass,nSuperClass);
    
  • 输出结果
2021-07-27 16:59:36.204223+0800 内存偏移[29797:756093] NBTeacher-0x10f015620
2021-07-27 16:59:36.204689+0800 内存偏移[29797:756093] NBPerson-0x10f0155d0
2021-07-27 16:59:36.204782+0800 内存偏移[29797:756093] NSObject-0x7fff86d50660
2021-07-27 16:59:36.204917+0800 内存偏移[29797:756093] NBTeacher - 0x10f0155f8 - NBPerson - 0x10f0155a8
2021-07-27 16:59:36.205001+0800 内存偏移[29797:756093] NBPerson - 0x10f0155a8 - NSObject - 0x7fff86d50638
2021-07-27 16:59:36.205082+0800 内存偏移[29797:756093] NSObject - 0x7fff86d50638 - NSObject - 0x7fff86d50660
2021-07-27 16:59:36.205176+0800 内存偏移[29797:756093] (null) - 0x0

  • NBTeacher元类的父类NBPerson的元类是同一个
  • NBPerson元类的父类NSObject的元类是同一个
  • NBTeacher 继承 NBPersonNBPerson 继承 NSObjectNSObject的父类是nil
  • NBTeacher元类 继承 NBPerson元类,NBPerson 继承 根元类根元类继承NSObject

三,类结构分析

查看objc4(818版本)的源码objc-runtime-new.h中,找到了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 getSuperclass() const {
#if __has_feature(ptrauth_calls)
#   if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
        if (superclass == Nil)
            return Nil;

#if SUPERCLASS_SIGNING_TREAT_UNSIGNED_AS_NIL
        void *stripped = ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
        if ((void *)superclass == stripped) {
            void *resigned = ptrauth_sign_unauthenticated(stripped, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
            if ((void *)superclass != resigned)
                return Nil;
        }
#endif
            
        void *result = ptrauth_auth_data((void *)superclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
        return (Class)result;

#   else
        return (Class)ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
#   endif
#else
        return superclass;
#endif
    }
  • 源码分析objc_class继承objc_objectobjc_object里面只有一个成员变量isa
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
  • 类的结构中也有隐藏的isa,占用8个字节

  • Class superclass是类的父类,结构体指针占用8个字节

  • cache_t cache是类的缓存空间,占用16个字节

  • class_data_bits_t保存类的数据,如属性方法等信息。

1,探究cache_t的大小

  • 搜索struct cache_t找到
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;//8字节
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;//4字节
#if __LP64__
            uint16_t                   _flags;//2字节 objc2走这个
#endif
            uint16_t                   _occupied;//2字节
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;//8字节
    };

#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    // _bucketsAndMaybeMask is a buckets_t pointer
    // _maybeMask is the buckets mask

    static constexpr uintptr_t bucketsMask = ~0ul;
    static_assert(!CONFIG_USE_PREOPT_CACHES, "preoptimized caches not supported");
    //   .........忽略与结构体内存无关的代码
  //源码位置为objc-runtime-new.h文件第338行-550行
  • typedef unsigned long uintptr_t是无符号长整形,占用8个字节。
  • preopt_cache_t *是结构体指针,占用8个字节。
  • uint16_t是无符号16位整形,占用2个字节。
  • mask_t是uint32_t类型的,占用4个字节。
  • cache_t的内存大小为:uintptr_t内存大小8个字节+union内存大小8个字节 = 16字节

所以

  • isa内存地址为首地址
  • superclass地址为首地址+0x08
  • cache_t地址为首地址+0x10
  • bits地址为首地址+0x20

2,class_data_bits_t bits结构体

class_data_bits_t bits结构体记录的是类的属性、成员变量以及方法。所以必须要了解结构体里面有什么,查找class_data_bits

struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) const
    {
        return bits & bit;
    }

    // Atomically set the bits in `set` and clear the bits in `clear`.
    // set and clear must not overlap.
    void setAndClearBits(uintptr_t set, uintptr_t clear)
    {
        ASSERT((set & clear) == 0);
        uintptr_t newBits, oldBits = LoadExclusive(&bits);
      //此处省略部分代码
public:

    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData)
    {
   //此处省略部分代码
  • class_rw_t 源码
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 nextSiblingClass;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:
    void setFlags(uint32_t set)
    {
        __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
    }

    void clearFlags(uint32_t clear) 
    {
        __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        ASSERT((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    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 *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    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_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    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_or_rw_ext)->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 *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    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 *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};
  • class_rw_t是结构体类型,提供了获取属性列表方法列表协议列表的方法。通过实例来验证下方法,属性,变量是不是在class_rw_t中,在类中添加属性和方法 以及成员变量,请继续往下看验证流程。

3,成员变量与类方法获取

  • 新建NBPerson
@interface NBPerson : NSObject{
    double height;
}

@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *nickName;

-(void)sayHello;
+(void)sayNB;


@end

@implementation NBPerson

-(void)sayHello{
    NSLog(@"hello----");
}
+(void)sayNB{
    NSLog(@"NB----");
}
@end

  • 运行
        NBPerson *nb = [NBPerson alloc];
        nb.name = @"nb";
        nb.nickName = @"nickeName";
        
  • 断点调试

(lldb) p/x NBPerson.class //当前类
(Class) $4 = 0x0000000100008268 NBPerson
(lldb) p/x 0x0000000100008268 + 0x20 //bits地址
(long) $5 = 0x0000000100008288
(lldb) p (class_data_bits_t *)0x0000000100008288
(class_data_bits_t *) $6 = 0x0000000100008288
(lldb) p $6->data() //获取到class_ro_t
(class_rw_t *) $7 = 0x0000000101315fb0
(lldb) p $7->properties() //往下找
(const property_array_t) $8 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000081e8
      }
      arrayAndFlag = 4295000552
    }
  }
}
(lldb) p $8.list 
(const RawPtr<property_list_t>) $9 = {
  ptr = 0x00000001000081e8
}
(lldb) p $9.ptr //找到属性列表地址
(property_list_t *const) $10 = 0x00000001000081e8

(lldb) p *$10 //获取属性列表数据
(property_list_t) $11 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $11.get(0)//找到第一个属性
(property_t) $12 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $11.get(1)//找到第二个属性
(property_t) $13 = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
(lldb) p $11.get(2)//没有第三个属性数组越界
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

  • 能找到name属性和nickname属性

  • 没有找到height成员变量

  • height跑哪里去了?接着找

  • class_rw_t中除了有属性,方法,协议以外,还有class_ro_t结构体指针类型的ro()

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
  • 接着上面的调试
lldb) p $7->ro()
(const class_ro_t *) $18 = 0x00000001000080a0
(lldb) p $18.ivars
(const ivar_list_t *const) $19 = 0x0000000100008180
  Fix-it applied, fixed expression was: 
    $18->ivars
(lldb) p *$19
(const ivar_list_t) $20 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $20.get(0)
(ivar_t) $21 = {
  offset = 0x0000000100008228
  name = 0x0000000100003ecf "height"
  type = 0x0000000100003f29 "d"
  alignment_raw = 3
  size = 8
}
(lldb) p $20.get(1)
(ivar_t) $22 = {
  offset = 0x0000000100008230
  name = 0x0000000100003ed6 "_name"
  type = 0x0000000100003f2b "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $20.get(2)
(ivar_t) $23 = {
  offset = 0x0000000100008238
  name = 0x0000000100003edc "_nickName"
  type = 0x0000000100003f2b "@\"NSString\""
  alignment_raw = 3
  size = 8
}

(lldb) p $20.get(3)//数组越界
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

总结
  • 查找到height成员变量和_name,_nickNmae成员变量

  • 成员变量底层实现是ivar_t,存储在class_ro_t成员变量列表

  • 系统是自动给属性添加_属性名的变量,存储在class_ro_t成员变量列表

  • 方法探究

(lldb) p $7->methods()
(const method_array_t) $24 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000080e8
      }
      arrayAndFlag = 4295000296
    }
  }
}
(lldb) p $24.list
(const method_list_t_authed_ptr<method_list_t>) $25 = {
  ptr = 0x00000001000080e8
}
(lldb) p $25.ptr
(method_list_t *const) $26 = 0x00000001000080e8
(lldb) p *$26
(method_list_t) $27 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
}
(lldb) p $27.get(0).big()
(method_t::big) $28 = {
  name = "sayHello"
  types = 0x0000000100003f21 "v16@0:8"
  imp = 0x0000000100003c90 (KCObjcBuild`-[NBPerson sayHello] at main.m:31)
}
(lldb) p $27.get(1).big()
(method_t::big) $29 = {
  name = "name"
  types = 0x0000000100003f37 "@16@0:8"
  imp = 0x0000000100003cc0 (KCObjcBuild`-[NBPerson name] at main.m:20)
}
  • 对象方法存储在NBPerson类中的方法列表method_list_t

  • 类方法没有在NBPerson的方法列表method_list_t里,类方法放在哪里呢?

发现使用get(index)的方式无法得到,使用get(index).big()才能获取,这是为什么呢?

探究property_tmethod_t

struct property_t {
    const char *name;
    const char *attributes;
};

struct method_t {
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    big &big() const {
        ASSERT(!isSmall());
        return *(struct big *)this;
    }

    SEL name() const {
        if (isSmall()) {
            return (small().inSharedCache()
                    ? (SEL)small().name.get()
                    : *(SEL *)small().name.get());
        } else {
            return big().name;
        }
    }
    const char *types() const {
        return isSmall() ? small().types.get() : big().types;
    }
    IMP imp(bool needsLock) const {
        if (isSmall()) {
            IMP imp = remappedImp(needsLock);
            if (!imp)
                imp = ptrauth_sign_unauthenticated(small().imp.get(),
                                                   ptrauth_key_function_pointer, 0);
            return imp;
        }
        return big().imp;
    }
  • 属性底层实现是property_t,在property_t结构体中定义了name等变量
  • 方法底层实现是method_t,在method_t结构体中定义了一个big(),通过big()获取SELIMP
继续查找方法
(lldb)  p $27.get(2).big()
(method_t::big) $31 = {
  name = ".cxx_destruct"
  types = 0x0000000100003f21 "v16@0:8"
  imp = 0x0000000100003d80 (KCObjcBuild`-[NBPerson .cxx_destruct] at main.m:29)
}
(lldb) p $27.get(3).big()
(method_t::big) $32 = {
  name = "setName:"
  types = 0x0000000100003f3f "v24@0:8@16"
  imp = 0x0000000100003cf0 (KCObjcBuild`-[NBPerson setName:] at main.m:20)
}
(lldb) p $27.get(4).big()
(method_t::big) $33 = {
  name = "nickName"
  types = 0x0000000100003f37 "@16@0:8"
  imp = 0x0000000100003d20 (KCObjcBuild`-[NBPerson nickName] at main.m:21)
}
(lldb) p $27.get(5).big()
(method_t::big) $34 = {
  name = "setNickName:"
  types = 0x0000000100003f3f "v24@0:8@16"
  imp = 0x0000000100003d50 (KCObjcBuild`-[NBPerson setNickName:] at main.m:21)
}
(lldb) p $27.get(6).big()
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 
  • method_list_t中有对象方法,属性的setter方法getter方法
  • method_list_t中没有获取到类方法
探究元类
(lldb) p/x object_getClass(NBPerson.class)
(Class) $0 = 0x0000000100008240
(lldb) p/x 0x0000000100008240 + 0x20 
(long) $1 = 0x0000000100008260
(lldb) p (class_data_bits_t*)0x0000000100008260
(class_data_bits_t *) $2 = 0x0000000100008260
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100627d70
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008080
      }
      arrayAndFlag = 4295000192
    }
  }
}
(lldb) p $4.list.ptr
(method_list_t *const) $5 = 0x0000000100008080
(lldb) p * $5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
  name = "sayNB"
  types = 0x0000000100003f21 "v16@0:8"
  imp = 0x0000000100003c60 (KCObjcBuild`+[NBPerson sayNB] at main.m:34)
}
  • 在元类中找到sayNB

  • object_getClass获取到JCPerson的元类

  • 元类中method_list_t中存储着类方法

总结:
  • 类的结构主要由isa,superclass,cache,bits组成

  • bits中存储着属性,方法,协议

  • 属性存储在property_list_t中,而成员变量存储在class_ro_t-->ivar_list_t,系统为属性自动生成的_属性名的变量也存储在class_ro_t-->ivar_list_t

  • 方法存储在method_list_t中,method_list_t主要存储着对象方法,属性的setter方法getter方法,而类方法存储在元类中的method_list_t