ios底层第一篇 alloc

49 阅读11分钟

目录

  • init方法的作用
  • new方法的作用
  • alloc流程
  • 内存对齐

init方法

@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@end

@implementation Person

@end


    Person * p = [Person alloc];
    NSLog(@"%ld", p.age);//0
    
    p.age = 20;
    NSLog(@"%ld", p.age);//20
    
    p = [p init];
    NSLog(@"%ld", p.age);//20

image.png

image.png

通过源码查看,无论是普通对象还是OC对象都是直接返回当前对象。

init方法其实默认啥也不做,但是我们可以通过重写init方法实现不同的业务逻辑。

new 方法

new方法就是调用alloc之后继续调用init方法。

image.png

//
//  ViewController.m
//  Demo1
//
//  Created by WANG on 2022/7/18.
//

#import "ViewController.h"

@interface ViewController ()

@end


@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.age = 1000;
    }
    return self;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    Person * p = [Person alloc];
//    Person * p = [Person new];
    NSLog(@"%ld", p.age);//0 1000
    
    p.age = 20;
    NSLog(@"%ld", p.age);//20 20
    
    p = [p init];
    NSLog(@"%ld", p.age);//20 1000
    
}
@end

alloc流程

alloc->objc_alloc

image.png

objc_alloc->inline callAlloc -> objc_msgSend(cls, alloc)

image.png

image.png

objc_msgSend(cls,alloc) -> objc_rootAlloc

image.png

objc_rootAlloc -> inline callAlloc -> allocWithZone

此时又调用了callAlloc方法,不过参数不同。

image.png

image.png

allocWithZone->objc_allocWithZone

static void 
fixupMessageRef(message_ref_t *msg)
{    
    msg->sel = sel_registerName((const char *)msg->sel);

    if (msg->imp == &objc_msgSend_fixup) { 
        if (msg->sel == @selector(alloc)) {
            msg->imp = (IMP)&objc_alloc;
        } else if (msg->sel == @selector(allocWithZone:)) {
            msg->imp = (IMP)&objc_allocWithZone;

objc_allocWithZone-> inline callAlloc -> _objc_rootAllocWithZone

image.png

此时又再次调用calloc,参数全部为true。

image.png

_objc_rootAllocWithZone -> inline _class_createInstanceFromZone。此时返回对象。

image.png

image.png

内存对齐

image.png

image.png

8字节对齐,如果小于16,则分配16个字节的内存大小。

另外malloc真实分配内存的时候,内部又进行了16字节对齐的算法。

对象的本质

结构体内存对齐

结构体的内存是成员变量的内存

        struct Person1{
            int age;
        }person1;

        NSLog(@"%lu", sizeof(person1));//4

结构体的成员变量的起始地址必须为当前变量的整数倍。

下面的height占用8个字节,必须保证地址为整数倍,之前占用了5个字节,又必须保证起始位置为8的倍数,因此从8开始。计算机中索引一般从0开始,所以需要跳过5,6,7。

        struct Person2{
            int age; // [0,1,2,3]
            char sex; // [4]
            double height; //[8,9,10,11,12,13,14,15]
        }person2;
        NSLog(@"%lu", sizeof(person2));//16

结构体的大小必须为最大成员变量的整数倍

下面代码实际使用了17个字节。但是最大成员变量为8。满足8的倍数,且大于17的最小整数为24。

        struct Person2{
            int age; // [0,1,2,3]
            char sex; // [4]
            double height; // [8,9,10,11,12,13,14,15]
            char firstname_firstcharactor; //[16]
        }person3;
        NSLog(@"%lu", sizeof(person3));//24

结构体嵌套结构体

嵌套结构体的起始地址为该结构体最大成员变量的倍数。

        struct P1 {
            int age;
            char sex;
        };
        
        struct Student{
            int grade;// 4 [0,1,2,3]
            double weight;// 4,5,6, [7,8,9,10,11,12,13,14,15]
            char sex1; // [16]
            
            /*
                结构体最大成员变量为4,4的倍数,且大于16,20
             age [20,21,22,23]
             sex [24]
             */
            struct P1 p;
        }s;
        // 结构体占了25个字节,大于25且为8的倍数,为32
        NSLog(@"%ld", sizeof(s));

OC对象的内存

OC对象的属性会自动进行排列节省内存。

其中gender会放最前面,age放第二个位置。

@interface Person : NSObject// isa 8
@property (nonatomic, copy) NSString *name; // 8
@property (nonatomic, copy) NSString *addr;//8
@property (nonatomic, assign) int age;//4
@property (nonatomic, assign) char gender;//1
@end

        Person *p =[Person new];
        NSLog(@"%ld", malloc_size((__bridge  void *)(p)));// 32

OC对象的成员变量不会自动重排。

// 48
@interface Person : NSObject// isa 8
{
    NSString *name;
    int age;
    NSString *addr;
    char gender;
}
// 32
@interface Person : NSObject// isa 8
{
    NSString *name;
    NSString *addr;
    int age;
    char gender;
}

继承中的成员变量,是可以使用父类中末尾的空余内存的。

@interface Person : NSObject// isa 8
{
    NSString *name;
    char gender;
    NSString *addr;
    int age;
}

@interface Student : Person
{
    char age1;
}
@end

//返回的值为40 48 40 48
#import <malloc/malloc.h>
#import <objc/runtime.h>

        Person *p =[Person new];
        NSLog(@"%zu",class_getInstanceSize([Person class]));
        NSLog(@"%ld", malloc_size((__bridge  void *)(p)));
        
        Student *s =[Student new];
        NSLog(@"%zu",class_getInstanceSize([Student class]));
        NSLog(@"%ld", malloc_size((__bridge  void *)s));


如果改成这样,则Person的默认没有空闲的内存。则返回的分别是32 32 40 48@interface Person : NSObject// isa 8
{
    NSString *name;
    char gender;
    int age;
    NSString *addr;
}
@end


OC对象的本质

新建一个mac命令行工程。

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

@implementation Person
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

clang -rewrite-objc main编译成C++代码,当前目录会生成一个main.cpp的文件。

typedef struct objc_object Person;

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};


struct Person_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name;
	NSInteger _age;
};

struct NSObject_IMPL {
	Class isa;
};

对象的内存分配

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    //计算需要的大小
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    
    // 此时,objc分配了内存,但是此时该内存空间还没内容。
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    // 这里开始修改isa指针信息。
    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}
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;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

ISA的结构

#     define ISA_BITFIELD                                                      \
        // 0 纯isa指针,1 包含isa,引用计数,对象的弱引用计数等信息。
        uintptr_t nonpointer        : 1;                                       \
        //是否有关联对象,如果有关联对象,则释放的时候会进行额外操作,会影响性能
        uintptr_t has_assoc         : 1;                                       \
        // 是否有dealloc函数,如果有,则释放对象需要额外的处理。
        uintptr_t has_cxx_dtor      : 1;                                       \
        // isa指针
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        // 用于调试器判断是真实的对象,还是未初始化的内存空间
        uintptr_t magic             : 6;                                       \
        // 该对象是否有弱引用,如果没有,则释放更快。
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        // 如果extrac_rc的引用计数太多了,导致溢出,则使用散列表存
        uintptr_t has_sidetable_rc  : 1;                                       \
        // 存放引用计数
        uintptr_t extra_rc          : 19

对象,类对象,元类对象

获取类对象的几种方式

        // 根据类获取class
        Class class1 = [Person class];
        // 根据对象获取class
        Class class2 = [Person new].class;
        //根据runtime获取class
        Class class3 = object_getClass([Person new]);
        Class class4 = objc_getClass("Person");
        
        NSLog(@"%p", class1);
        NSLog(@"%p", class2);
        NSLog(@"%p", class3);
        NSLog(@"%p", class4);

元类对象

  • 所有元类对象的isa(包括NSObject的元类对象本身)都指向NSObject的元类对象。

  • NSObject的元类的父类为NSObject的类对象。

  • NSObject的类对象的父类为nil。

  • OC对象的元类的父类为父类的元类。

        Class metaClass = objc_getMetaClass("Person");
        Class metaClass1 = object_getClass([Person class]);
        // 通过class继续获取class,还是类对象。针对类对象获取class,直接返回self。
        Class class5 = [[Person class] class];

类对象和元类对象

都是class类型,内部实现是一样的。

//objc_object为isa
// isa 8字节,
// superclass 8字节
// cache 16字节
// bits
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中bits的内容

@interface Person : NSObject
{
    int age;
}
@property (nonatomic, assign) int age1;

- (void)sayHello;

+ (void)sayHi;

@end

@implementation Person

- (void)sayHello{
    NSLog(@"say hello");
}

+ (void)sayHi {
    NSLog(@"say hi");
}
@end

在NSLog的地方进行断点调试。

        Person *p = [[Person alloc] init];
        
        NSLog(@"Hello world");

获取类对象的地址

(lldb) p p.class
(Class) $0 = 0x0000000100008258

根据内存平移,获取类对象的Bits

(lldb) x/6gx $0
0x100008258: 0x0000000100008230 0x0000000100839140
0x100008268: 0x0003000100b9fdb0 0x0001801800000000
0x100008278: 0x0000000100c54234 0x00000001008390f0

// 其中,isa,superClass占8字节,cache占16字节,所以bits从32字节开始
0x0000000100c54234该值为类对象对应的bits,bits的地址为0x100008278

将bits的地址进行类型转换

// 由于bits的内部实现只有一个无符号长整数,所以需要进行类型转换,根据该类的方法查看对应的值
struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;//unsigned long类型
// 根据地址,获取class_data_bits_t的指针对象
(lldb) p/x (class_data_bits_t *)0x100008278
(class_data_bits_t *) $1 = 0x0000000100008278
// 通过指针查看值,这个值还是不太好理解。
p *$1
(class_data_bits_t) $2 = (bits = 4307588740)
class_data_bits_t提供了方法查看数据。
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

p $1->data()
(class_rw_t *) $4 = 0x0000000100c09680
// 查看class_rw_t的数据
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000232
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x000000020b2b4798
}

// 上面的数据不方便查看,但是提供了几个方法
    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};
        }
    }
    

查看methods

(lldb) p $5.methods()
(const method_array_t) $6 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003ec8
      }
      arrayAndFlag = 4294983368
    }
  }
}
(lldb) p $6.list
(const method_list_t_authed_ptr<method_list_t>) $8 = {
  ptr = 0x0000000100003ec8
}
p $8.ptr
(method_list_t *const) $9 = 0x0000000100003ec8
// 此时,可以查看到方法列表的count为3,表示有3个方法。
(lldb) p *$9
(method_list_t) $10 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 3)
}
// 容器类型,通过get(index)获取里面的值
(lldb) p $10.get(0)
(method_t) $11 = {}
//大端和小端
0x12345678,地址由低到高的存储
大端模式的存储, 12 34 56 78
小端模式的存储 78 56 34 12

intel芯片采用的是大端,arm采用的是小端。

(lldb) p $11.small()
(method_t::small) $12 = {
  name = (offset = 17176)
  types = (offset = 163)
  imp = (offset = -420)
}

// small查看到的是原始数据,但是不好理解,使用getDescription可以帮我们分析
(lldb) p $11.getDescription()
(objc_method_description *) $13 = 0x0000000100c83670
// 取指针的值
(lldb) p *$13
(objc_method_description) $14 = (name = "sayHello", types = "v16@0:8")

查看所有的方法

(lldb) p *($10.get(0).getDescription())
(objc_method_description) $15 = (name = "sayHello", types = "v16@0:8")
(lldb) p *($10.get(1).getDescription())
(objc_method_description) $16 = (name = "age1", types = "i16@0:8")
(lldb) p *($10.get(2).getDescription())
(objc_method_description) $17 = (name = "setAge1:", types = "v20@0:8i16")
(lldb) p *($10.get(3).getDescription())
Assertion failed: (i < count), function get, file objc-runtime-new.h, line 629.

获取属性

(lldb) p p.class
(Class) $0 = 0x0000000100008240
(lldb) x/6gx $0
0x100008240: 0x0000000100008268 0x0000000100839140
0x100008250: 0x0003000100c04f90 0x0001801800000000
0x100008260: 0x0000000100c045d4 0x00000001008390f0
(lldb) p (class_data_bits_t *)0x100008260
(class_data_bits_t *) $1 = 0x0000000100008260
(lldb) p *$1
(class_data_bits_t) $2 = (bits = 4307568084)
(lldb) p $2.data()
(class_rw_t *) $3 = 0x0000000100c045d0
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000232
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x000000020b2b4798
}
(lldb) p $4.properties()
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008090
      }
      arrayAndFlag = 4295000208
    }
  }
}
(lldb) p $5
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008090
      }
      arrayAndFlag = 4295000208
    }
  }
}
(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
  ptr = 0x0000000100008090
}
(lldb) p $6.ptr
(property_list_t *const) $7 = 0x0000000100008090
(lldb) p *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 1)
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "age1", attributes = "Ti,N,V_age1")
(lldb) p $8.get(1)

获取成员变量

根据类对象获取bits地址

(lldb) p p.class
(Class) $0 = 0x0000000100008240
(lldb) x/6gx $0
0x100008240: 0x0000000100008268 0x0000000100839140
0x100008250: 0x0003000100ca2b70 0x0001801800000000
0x100008260: 0x0000000100b71554 0x00000001008390f0

根据bits地址获取class_rw_t

(lldb) p (class_data_bits_t *)0x100008260
(class_data_bits_t *) $1 = 0x0000000100008260
(lldb) p *$1
(class_data_bits_t) $2 = (bits = 4306965844)
(lldb) p $2.data()
(class_rw_t *) $3 = 0x0000000100b71550
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000232
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x000000020b2b4798
}

根据class_rw_t获取class_ro_t

(lldb) p $4.ro()
(const class_ro_t *) $5 = 0x00000001000080a8
(lldb) p *$5
(const class_ro_t) $6 = {
  flags = 128
  instanceStart = 8
  instanceSize = 16
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "Person" {
      Value = 0x0000000100003f4e "Person"
    }
  }
  baseMethods = {
    ptr = 0x0000000100003ea0
  }
  baseProtocols = nil
  ivars = 0x0000000100008048
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008090
  _swiftMetadataInitializer_NEVER_USE = {}
}

根据class_ro_t获取成员变量

(lldb) p $6.ivars
(const ivar_list_t *const) $7 = 0x0000000100008048
(lldb) p *$7
(const ivar_list_t) $8 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 2)
}
(lldb) p $8.get(0)
(ivar_t) $9 = {
  offset = 0x0000000100008228
  name = 0x0000000100003efa "age"
  type = 0x0000000100003f83 "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $8.get(1)
(ivar_t) $10 = {
  offset = 0x000000010000822c
  name = 0x0000000100003efe "_age1"
  type = 0x0000000100003f83 "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $8.get(2)
Assertion failed: (i < count), function get, file objc-runtime-new.h, line 629.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

class_rw_tclass_ro_t

其中class_ro_t是程序编译的时候确定的ro表示readonly只读的。由于runtime的特性,我们可以动态的添加方法。因此需要class_rw_t结构,rw表示readwrite可读可写的。

class_rw_ext

早期的runtime会将类的方法重新加载一遍。新的版本为了效率,直接使用class_rw_t包含class_ro_t。因为大部分类都不需要修改。

对于需要修改的类,比如添加分类,使用runtime添加方法。则会创建一个class_rw_ext

比如获取方法的时候,就需要先判断一下。到底是否使用了class_rw_ext

    const method_array_t methods() const {
        // 获取当前的ro或者rwe
        auto v = get_ro_or_rwe();
        // 如果是rwe,获取rwe中的数据
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
        // 如果是ro_t,则直接获取class_ro_t的数据
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods};
        }
    }

获取类方法

获取类对象的地址

(lldb) x/gx p.class
0x100008240: 0x0000000100008268

获取元类对象的地址

(lldb) p/x 0x0000000100008268 & 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x0000000100008268

元类的isa加32为bits

(lldb) p (class_data_bits_t *)0x0000000100008288
(class_data_bits_t *) $2 = 0x0000000100008288
(lldb) p *$2
(class_data_bits_t) $3 = (bits = 4305657012)

获取class_rw_t

(lldb) p $3.data()
(class_rw_t *) $4 = 0x0000000100a31cb0
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000064
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x000000020b2b5bc0
}

获取方法列表

(lldb) p $5.methods()
(const method_array_t) $6 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003e70
      }
      arrayAndFlag = 4294983280
    }
  }
}
(lldb) p $6.list
(const method_list_t_authed_ptr<method_list_t>) $7 = {
  ptr = 0x0000000100003e70
}
(lldb) p $7.ptr
(method_list_t *const) $8 = 0x0000000100003e70
(lldb) p *$8
(method_list_t) $9 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 1)
}

获取所有的方法

(lldb) p $9.get(0).small()
(method_t::small) $10 = {
  name = (offset = 17288)
  types = (offset = 228)
  imp = (offset = -332)
}
(lldb) p $9.get(0).getDescription()
(objc_method_description *) $11 = 0x0000000100c30550
(lldb) p *($9.get(0).getDescription())
(objc_method_description) $12 = (name = "sayHi", types = "v16@0:8")
(lldb) p *($9.get(1).getDescription())
Assertion failed: (i < count), function get, file objc-runtime-new.h, line 629.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

cache_t

用于方法的缓存。

cache_t的结构

struct cache_t {
private:
    // 8字节 usigned long类型
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            // typedef uint32_t mask_t;4字节
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__ // 表示64比特指针,也就是常说的64位系统
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
         指针,占用8字节
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };

因此一个cache_t占16字节。

获取cache_t中的方法缓存

获取类对象的地址

(lldb) x/4gx p.class
0x100008240: 0x0000000100008268 0x0000000100839140
0x100008250: 0x0003000100b217d0 0x0001801800000000

类对象的地址加16就是cache_t的地址。

(lldb) p/x (cache_t *)0x100008250
(cache_t *) $1 = 0x0000000100008250
(lldb) p *$1
(cache_t) $2 = {
  _bucketsAndMaybeMask = {
    std::__1::atomic<unsigned long> = {
      Value = 844429236770768
    }
  }
   = {
     = {
      _maybeMask = {
        std::__1::atomic<unsigned int> = {
          Value = 0
        }
      }
      _flags = 32792
      _occupied = 1
    }
    _originalPreoptCache = {
      std::__1::atomic<preopt_cache_t *> = {
        Value = 0x0001801800000000
      }
    }
  }
}

获取cache_t的mask。

(lldb) p $2.mask()
(mask_t) $3 = 3

获取cache_t的容量,,mask+1,如果mask为0,则容量也是0.

(lldb) p $2.capacity()
(unsigned int) $4 = 4

获取cache_t已使用的容量,1表示使用了2个。

(lldb) p $2.occupied()
(mask_t) $5 = 1

方法缓存是bucket_t类型,底层是以数组的形式存放所有的bucket_t。

(lldb) p $2.buckets()
(bucket_t *) $6 = 0x0000000100b217d0

指针加1,获取下一个bucket_t的地址

(lldb) p ($6+1)
(bucket_t *) $7 = 0x0000000100b217e0
(lldb) p *($6+1)
(bucket_t) $8 = {
  _imp = {
    std::__1::atomic<unsigned long> = {
      Value = 8465732
    }
  }
  _sel = {
    std::__1::atomic<objc_selector *> = "" {
      Value = ""
    }
  }
}

根据bucket_t的地址获取对应的方法和方法实现。

(lldb) p (*($6+1)).sel()
(SEL) $9 = "class"
(lldb) p (*($6+1)).imp(nil,p.class)
(IMP) $10 = 0x000000010081af04 (libobjc.A.dylib`-[NSObject class] at NSObject.mm:2254)
(lldb) p (*($6+0)).imp(nil,p.class)
(IMP) $11 = 0x000000010081b314 (libobjc.A.dylib`-[NSObject respondsToSelector:] at NSObject.mm:2310)
(lldb) p (*($6+0)).sel()
(SEL) $12 = "respondsToSelector:"
(lldb) p (*($6+2)).sel()
(SEL) $13 = (null)
(lldb) p (*($6+2)).sel()

方法缓存的过程

void cache_t::insert(SEL sel, IMP imp, id receiver)

void cache_t::insert(SEL sel, IMP imp, id receiver)
{
    runtimeLock.assertLocked();

    // Never cache before +initialize is done
    // 如果该类还没初始化,则不做任何操作,+initialize在第一次消息发送时调用
    if (slowpath(!cls()->isInitialized())) {
        return;
    }

    if (isConstantOptimizedCache()) {
        _objc_fatal("cache_t::insert() called with a preoptimized cache for %s",
                    cls()->nameForLogging());
    }

#if DEBUG_TASK_THREADS
    return _collecting_in_critical();
#else
#if CONFIG_USE_CACHE_LOCK
    mutex_locker_t lock(cacheUpdateLock);
#endif

    ASSERT(sel != 0 && cls()->isInitialized());

    // Use the cache as-is if until we exceed our expected fill ratio.
    // 插入数据的是,使用量需要加1
    mask_t newOccupied = occupied() + 1;
    
    // 容量,旧的容量都是当前的容量
    unsigned oldCapacity = capacity(), capacity = oldCapacity;
    
    // 如果容量为0,则扩容
    if (slowpath(isConstantEmptyCache())) {
        // Cache is read-only. Replace it.
        //arm64为2
        if (!capacity) capacity = INIT_CACHE_SIZE;
        reallocate(oldCapacity, capacity, /* freeOld */false);
    }
    // CACHE_END_MARKER arm64为0,其它平台为1
    // arm64为0,cache_fill_ratio在arm64为7/8,其它平台为3/4
    else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
        // Cache is less than 3/4 or 7/8 full. Use it as-is.
    }
#if CACHE_ALLOW_FULL_UTILIZATION //arm64为1,其它平台为0
    // FULL_UTILIZATION_CACHE_SIZE为8
    else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {
        // Allow 100% cache utilization for small buckets. Use it as-is.
    }
#endif
    else {
        // 两倍扩容
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            // MAX_CACHE_SIZE 1<<16 64K
            capacity = MAX_CACHE_SIZE;
        }
        reallocate(oldCapacity, capacity, true);
    }

    // 获取bucket_t的指针
    bucket_t *b = buckets();
    // mask,也可以通过mask()函数获取
    mask_t m = capacity - 1;
    
    // 根据方法名称获取插入的位置
    //static inline mask_t cache_hash(SEL sel, mask_t mask) 
    //{
    //    uintptr_t value = (uintptr_t)sel;
    //#if CONFIG_USE_PREOPT_CACHES
    // //在iOS真机中,为了防止hash冲突,进行了异或操作。
    //    value ^= value >> 7;
    //#endif
    //    return (mask_t)(value & mask);
    //}
    mask_t begin = cache_hash(sel, m);
    mask_t i = begin;

    // 当前应该插入的位置可能有值,如果有值,说明出现了hash冲突,依次在下一个位置尝试插入
    // Scan for the first unused slot and insert there.
    // There is guaranteed to be an empty slot.
    do {
        if (fastpath(b[i].sel() == 0)) {
            // 当前位置没有值,可以插入
            
            // 使用量加1
            incrementOccupied();
            // 缓存
            b[i].set<Atomic, Encoded>(b, sel, imp, cls());
            return;
        }
        if (b[i].sel() == sel) {
            // 有缓存了,则不需要再次缓存
            // The entry was added to the cache by some other thread
            // before we grabbed the cacheUpdateLock.
            return;
        }
    } while (fastpath((i = cache_next(i, m)) != begin));

    bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}