OC 底层探索 01、如何探索底层 + alloc 做了什么
本文介绍 如何探索 alloc 和 alloc 做了什么?
从最简单的代码开始:
MyPerson *p1 = [MyPerson alloc]; MyPerson *p2 = [p1 init]; MyPerson *p3 = [p1 init]; NSLog(@"%@ - %p - %p",p1,p1,&p1);// p1 - 对象 - 指针 NSLog(@"%@ - %p - %p",p2,p2,&p2); NSLog(@"%@ - %p - %p",p3,p3,&p3);
打印结果如下:
p1/p2/p3:对象是同一个MyPerson,指针指的同一个对象,指针地址不同。且其指针地址相差 8 字节,为什么呢?
alloc 做了什么?我们点击 alloc 无法查看实现,只能停留在 NSObject.h 中?
一、如何查找实现ku?
查找实现库
1、符号断点:
run: --> [NSObject alloc] --> libobjc.A.dylib
2、普通断点
按住:control + step into -->
添加符号断点:objc_alloc --> libobjc.A.dylib
3、汇编查看
debug->debug workflow -> always show disassembly
运行:
control + step into --> 如下图 --> 添加 符号断点 objc_alloc --> libobjc.A.dylib
objc 源码下载 - 地址:最新版本 objc4-781
二、进入源码 -- 流程分析
打开下载的源码文件,由 C C++ 汇编共同编写。
1、源码分析
1.1)alloc 入口:
我们通过点进去,发现:
_objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone / objc_msgSend 2者走谁?
callAlloc(Class cls, bool checkNil, bool allocWithZone=false) { #if __OBJC2__ if (slowpath(checkNil && !cls)) return nil; if (fastpath(!cls->ISA()->hasCustomAWZ())) { return _objc_rootAllocWithZone(cls, nil); } #endif // No shortcuts available. if (allocWithZone) { return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil); } return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)); }
可通过:回到 demo,添加符号断点:_objc_rootAlloc / callAlloc / _objc_rootAllocWithZone --> 运行
跟随断点,可知 alloc 走 _objc_rootAllocWithZone:
1.2)开辟空间 _objc_rootAllocWithZone()
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused) { // allocWithZone under __OBJC2__ ignores the zone parameter return _class_createInstanceFromZone(cls, 0, nil, OBJECT_CONSTRUCT_CALL_BADALLOC); }
_class_createInstanceFromZone() --> 源码中 3 个方法(标红位置)
_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; // 1: 算出要开辟多少内存 size = cls->instanceSize(extraBytes); if (outAllocatedSize) *outAllocatedSize = size; id obj; if (zone) {// zone 已经废弃不走它 直接走 else obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size); } else { // 2: 去申请内存,返回 地址指针 obj = (id)calloc(1, size); } if (slowpath(!obj)) { if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) { return _objc_callBadAllocHandler(cls); } return nil; } // 3: 指向内存的地址指针 和 cls 关联 起来 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); }
思考,是否可以直接运行源码走进方法进行调试呢?
--> objc 源码编译调试 配置方法
配置成功后,run。
2、实际运行分析
2.1)开辟多少内存空间 instanceSize()
align16() --> size + 0 - 8 = 8; // size = 16
static inline size_t align16(size_t x) { return (x + size_t(15)) & ~size_t(15); }
字节对齐 (针对对象)-- 最新的是16字节对齐,苹果之前的对齐方式是8字节对齐。16字节更加安全,预留空间更多,不易产生野指针等。
每个对象都有继承自NSObject 的 isa,一个指针8字节。
2.2)申请开辟内存空间 calloc() --> 2.3)内存指针和 类 cls 绑定 initInstanceIsa()
三、init 和 new
init:
构造方法(工厂),用来给我们自定义开发 - 重写。
new: --> alloc 的 callAlloc --> [MyPerson new]; ==》相当于 [[MyPerson alloc] init];
但,我们 init 重写的一些方法,在new是无法实现的。 <-- 不同之处
以上。
tip:slowpath / fastpath 是什么? -- 编译器优化
例如一些中间过程编译直接优化掉,节省时间,优化性能。
如上图,release 是 Feastest,Smallest, 发包苹果也会帮我们进行优化。
运行:
我们将 debug 也改成 fastest,smallest,再次运行,可看到中间编译过程已被优化掉: