底层原理

111 阅读4分钟

objc4-781源码的编译与调试

objc4-781源码

依赖:

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

image.png

对象创建

  1. alloc init new方法的探索
  2. 对象的创建

image.png

计算内存大小时,如果缓存中存在,则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

ISA底层探索1

ISA底层探索2

类结构

类结构分析1

类结构分析2

方法缓存

方法缓存原理1

方法缓存原理2

方法查找

快查找

慢查找

消息转发1

查找原理

消息转发2

dyld与objc之间的关联

类加载

类的加载机制

分类加载

分类加载

扩展与关联对象

扩展和关联对象

Block

Block

多线程

  1. dispatch_once原理

KVO

  1. 探寻KVO本质
  2. KVO实现
  3. KVO原理分析

KVC

  1. KVC实现

组件化

- 组件化方案:https://www.jianshu.com/p/2e62e12a7dfc
- CTMediator实现细节:https://www.jianshu.com/p/98494a181361

JS交互

参考:www.jianshu.com/p/15af43534…