本文介绍 如何探索 alloc 和 alloc 做了什么?
objc 可编译源码
从最简单的代码开始:
1 2 3 4 5 6 7 MyPerson *p1 = [MyPerson alloc ] ; MyPerson *p2 = [p1 init ] ; MyPerson *p3 = [p1 init ] ; NSLog(@"%@ - %p - %p" ,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 者走谁?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 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()
1 2 3 4 5 6 7 id_objc_rootAllocWithZone (Class cls, malloc_zone_t *zone __unused ) { return _class_createInstanceFromZone (cls, 0 , nil , OBJECT_CONSTRUCT_CALL_BADALLOC); }
_class_createInstanceFromZone() –> 源码中 3 个方法 (标红位置)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 _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 ()); 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); } if (slowpath (!obj)) { if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) { return _objc_callBadAllocHandler(cls); } return nil; } if (!zone && fast) { obj->initInstanceIsa (cls, hasCxxDtor); } else { 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
1 2 3 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,再次运行,可看到中间编译过程已被优化掉: