objc4-781源码的编译与调试
依赖:
- dyld-733.6
- launchd-106.10
- Libc-583
- Libc-825.24
- libclosure-74
- libplatform-220
- xnu-6153.11.26
- libpthread-416.11.1
objc_object与objc_class
- objc_object
- isa指针
- 方法
- 一些关于
isa的函数,如initIsa()、getIsa()、changeIsa()等 - 一些弱引用的函数,如
isWeaklyReferenced()、setWeaklyReferenced_nolock()等 - 一些内存管理函数,如
retain()、release()、autorelease()等 - 两个关联对象函数,分别是
hasAssociatedObjects()和setHasAssociatedObjects
- 一些关于
- objc_class: 继承objc_object
- 父类指针:superClass
- 调用方法缓存、实例对象缓存
cache_t cache - 存储类信息的结构体:
class_data_bits_t bits
对象创建
计算内存大小时,如果缓存中存在,则16字节对齐,否则是8字节对齐
申请内存空间时,是16字节对齐
系统类创建对象时(例如NSObject),只会走一次callAlloc,因为有缓存
自定义类创建对象时,因为没有缓存,会走两次callAlloc
第一次会走msgSend
第二次会走缓存
调用alloc会走objc_alloc的原因是当启动的时候read_image会修改调用imp的地址
static void
fixupMessageRef(message_ref_t *msg)
{
msg->sel = sel_registerName((const char *)msg->sel);
if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == SEL_alloc) {
msg->imp = (IMP)&objc_alloc;
} else if (msg->sel == SEL_allocWithZone) {
msg->imp = (IMP)&objc_allocWithZone;
} else if (msg->sel == SEL_retain) {
msg->imp = (IMP)&objc_retain;
} else if (msg->sel == SEL_release) {
msg->imp = (IMP)&objc_release;
} else if (msg->sel == SEL_autorelease) {
msg->imp = (IMP)&objc_autorelease;
} else {
msg->imp = &objc_msgSend_fixedup;
}
}
...
}
内存对齐与分配
sizeof():计算数据类型占用的内存大小,其参数可以传基本数据类型、对象类型、指针类型,对于类似于int这样的基本数据而言,sizeof获取的就是数据类型占用的内存大小,不同的数据类型所占用的内存大小是不一样的;class_getInstanceSize()是用来计算实例对象 实际占用的内存大小,采用的是8字节对齐的方式进行运算的,也就是说对象实际占用的内存大小是8的倍数,其实现源码如下所示:
#ifdef __LP64__
# define WORD_MASK 7UL
#else
# define WORD_MASK 3UL
#endif
size_t class_getInstanceSize(Class cls){
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
malloc_size()是用来计算实例对象分配的内存大小,其是由系统完成的,采用的是16字节对齐的方式进行运算的,也就是说对象实际占用的内存大小是16的倍数;
结构体内存对齐
- 每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数),我们可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来修改这一系数,其中的n就是你要指定的“对齐系数”,在iOS中,Xcode默认为#pragma pack(8),即8字节对齐;
- 结构体内存对齐的规则:
- 规则一: 结构体第一个数据成员的起始地址是在结构体内存地址偏移量offset=0的位置,然后依次排列其他数据成员,其他数据成员必须满足当前数据成员的起始位置(结构体内存地址偏移量offset)是当前数据成员本身内存大小的整数倍;
- 规则二:数据成员为结构体:即当结构体中嵌套了结构体时,必须满足作为数据成员的结构体的起始位置是其最大成员内存大小的整数倍,比如结构体a嵌套结构体b,b中有char、int、double等,则b的最大成员内存大小为8;
- 规则三:最后结构体的内存大小必须是结构体中最大成员内存大小的整数倍,不足的需要补齐;
属性重排
- 大小端模式:“大端模式:数据的高字节存储在内存的低地址中;小端模式:数据的低字节存储在内存的低地址中; 苹果是小端模式。
- 系统会进行属性重排,内存结构和属性顺序不一定一致
最终总结:
- OC对象在计算实际占用内存大小时采用8字节对齐,即调用了
class_getInstanceSize()函数进行计算的; - OC对象在计算实际分配内存大小采用的16字节对齐,即调用了
calloc()函数或者malloc_size()函数;位运算的算法:(size + 16 - 1) >> 4 << 4 实现了16字节对齐; - 系统为了优化内存,会在每个对象内部进行属性重排,并使用8字节对齐,使单个对象占用的内存资源尽可能的小;
ISA
类结构
方法缓存
方法查找
dyld与objc之间的关联
类加载
分类加载
扩展与关联对象
Block
多线程
KVO
KVC
组件化
- 组件化方案:https://www.jianshu.com/p/2e62e12a7dfc
- CTMediator实现细节:https://www.jianshu.com/p/98494a181361