1. alloc流程分析
打开符号断点_objc_rootAlloc
、callAlloc
、_objc_rootAllocWithZone
1). alloc
走这个返回分支的时候
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
objc_msgSend
会走下面这个方法
[BYPerson alloc]
走下面的
// Calls [cls alloc].
id objc_alloc(Class cls)
2). init / new
3). callAlloc
普通类先走objc_msgSend
,再走_objc_rootAllocwithZone
,NSObject直接走_objc_rootAllocwithZone
判定cls->ISA()->customAWZ
NSObject存在DEFAULT_AWZ
所以customAWZ
为false
,普通类不存在DEFAULT_AWZ
所以customAWZ
为true
,故走下面的objc_msgSend
回到 ==> 1). alloc,所以这也解释了为什么NSObject只走一遍callAlloc
而普通类要走两遍。
4). _objc_rootAllocWithZone / _class_createInstanceFromZone
计算空间,申请内存,关联isa
5). instanceSize 计算空间
instanceSize(extraBytes)
计算需要开辟的内存大小,传入的extraBytes 为 0
,对象最小空间为16,所以需要16字节对齐,isa指针占用1个字节,8bit
6). initInstanceIsa / initIsa
2. isa
旧版ISA()
实现
新版ISA()
需要认证
ISA_MASK
等一些定义
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
isa_t
简化结构
// 联合体,每次只能使用其中某一个成员
union isa_t {
Class cls;
uintptr_t bits;
// arm64 架构
struct {
uintptr_t nonpointer : 1; // 0:普通指针,1:优化过,使用位域存储更多信息
uintptr_t has_assoc : 1; // 对象是否含有或曾经含有关联引用
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析构函数或OC的dealloc
uintptr_t shiftcls : 33; // 存放着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 来存储引用计数
uintptr_t extra_rc : 19; // 引用计数能够用 19 个二进制位存储时,直接存储在这里
};
};
// x86_64 架构
struct {
uintptr_t nonpointer : 1; // 0:普通指针,1:优化过,使用位域存储更多信息
uintptr_t has_assoc : 1; // 对象是否含有或曾经含有关联引用
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析构函数或OC的dealloc
uintptr_t shiftcls : 44; // 存放着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 来存储引用计数
uintptr_t extra_rc : 8; // 引用计数能够用 8 个二进制位存储时,直接存储在这里
};
-
nonpointer
0,代表普通的指针,存储着Class
、Meta-Class
对象的内存地址
1,代表优化过,使用位域存储更多的信息,bits & ISA_MASK
可获得真实的isa指针地址 -
has_assoc
是否有设置过关联对象,如果没有,释放时会更快 -
has_cxx_dtor
是否有C++的析构函数(.cxx_destruct
),如果没有,释放时会更快 -
shiftcls
存储着Class
、Meta-Class
对象的内存地址信息,bits & ISA_MASK
可获得真实的isa指针地址 -
magic
用于在调试时分辨对象是否未完成初始化 -
weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快 -
deallocating
对象是否正在释放 -
extra_rc
里面存储的值是引用计数器减1 -
has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
extra_rc
和has_sidetable_rc
详细解析参见 ==> 内存管理
isa与superclass走向
3. 相关问题
1). NSObject和自定义类为啥不走alloc而是走objc_alloc
llvm会将一些方法hook,当调用的时候,会替换成其他方法,所以当我们调用alloc
的时候,其实真实调用的是objc_alloc
类似的方法还有以下这些:
// This is the table of ObjC "accelerated dispatch" functions. They are a set
// of objc methods that are "seldom overridden" and so the compiler replaces the
// objc_msgSend with a call to one of the dispatch functions. That will check
// whether the method has been overridden, and directly call the Foundation
// implementation if not.
// This table is supposed to be complete. If ones get added in the future, we
// will have to add them to the table.
const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
"objc_alloc",
"objc_autorelease",
"objc_release",
"objc_retain",
"objc_alloc_init",
"objc_allocWithZone",
"objc_opt_class",
"objc_opt_isKindOfClass",
"objc_opt_new",
"objc_opt_respondsToSelector",
"objc_opt_self",
};
2). NSObject为啥只走一遍callAlloc而普通类要走两遍
见 ==> 3). callAlloc
4. 一些操作
1). 字节对齐
- 位移
size_t align16(size_t x) {
return (x + size_t(15)) >> 4 << 4;
}
- 位与和取反
size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
2). 内存占用
-
sizeof
计算类型占用空间,指针*p -> 8,double
-> 8,int
-> 4,char
-> 1 -
class_getInstanceSize
实例对象占用内存的大小,实例对象的成员变量实际所需大小 -
malloc_size
为对象申请内存的大小,会进行16 字节对齐,不小于class_getInstanceSize
具体见 ==> iOS - 获取内存大小的三种方式