目录
- 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
通过源码查看,无论是普通对象还是OC对象都是直接返回当前对象。
init方法其实默认啥也不做,但是我们可以通过重写init方法实现不同的业务逻辑。
new 方法
new方法就是调用alloc之后继续调用init方法。
//
// 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
objc_alloc
->inline callAlloc
-> objc_msgSend(cls, alloc)
objc_msgSend(cls,alloc)
-> objc_rootAlloc
。
objc_rootAlloc
-> inline callAlloc
-> allocWithZone
此时又调用了callAlloc方法,不过参数不同。
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
此时又再次调用calloc,参数全部为true。
_objc_rootAllocWithZone
-> inline _class_createInstanceFromZone
。此时返回对象。
内存对齐
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_t
和class_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
}