main函数的加载流程
1.在int main前面打个断点
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) { //在这一行前面打个断点
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
然后增加一个_objc_init的符号断点
记得关闭左下角show only stack frames with debug symbols and between libraries的按钮
可以看到Xcode左侧的调用堆栈视图如下图:
通过上面的调用堆栈信息,可以得出一个简略的加载流程:
alloc&init初步探索
在ViewController.m文件中调用alloc和init方法:
NSObject *p1 = [NSObject alloc];
NSObject *p2 = [p1 init];
NSObject *p3 = [p1 init];
NSLog(@"%@ - %p", p1, &p1);
NSLog(@"%@ - %p", p2, &p2);
NSLog(@"%@ - %p", p3, &p3);
可以发现打印出来是这样:
打印出来的是同一个对象,p1,p2,p3的指针地址是不同的,但指向同一块内存,说明alloc已经创建了对象,那么对象是怎么创建的呢?
这时我们需要查看alloc的源码实现。
按住command和control键,点击alloc,进入头文件NSObject.h,只能看到alloc方法的声明,未能找到alloc方法的实现,看起来无从下手。
可以通过添加符号断点来解决这个问题,选择 Symbolic Breakpoint,在Symbol那一栏添加objc_alloc
运行程序,可以看到我们需要查找的动态链接库为libobjc.A.dylib:
探索libobjc源码
下载libobjc源码,进行配置,然后新建一个命令行项目,运行如下代码:
NSObject *obj = [NSObject alloc];
下符号断点objc_alloc,首先进入的是objc_alloc:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
然后会来到callAlloc方法,注意此时第三个参数的值为false:
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
// 判断传入的 checkNil 是否进行判空操作
if (slowpath(checkNil && !cls)) return nil;
// 如果当前编译环境为 OC 2.0
#if __OBJC2__
// 当前类没有自定义的 allocWithZone
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// 既没有实现 alloc,也没有实现 allocWithZone 就会来到这里,下面直接进行内存开辟操作。
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
// 修复没有元类的类,用人话说就是没有继承于 NSObject
// 判断当前类是否可以快速开辟内存,注意,这里永远不会被调用,因为 canAllocFast 内部
// 返回的是false
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
由于allocWithZone为false且cls为NSObject,所以会来到最后一句return [cls alloc];
,接下来是alloc方法:
+ (id)alloc {
return _objc_rootAlloc(self);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
这里第二次调用callAlloc方法,但参数不一样,第二个参数 checkNil 是 false ,因为前面在第一次调用 callAlloc 的时候进行了判空,所以这里没必要再次进行判空了。第三个参数 allocWithZone 传的是 true ,其实 allocWithZone 本质上和 alloc 是没有区别的,只是在 Objective-C 远古时代,程序员需要使用诸如 allocWithZone 来优化对象的内存结构,而在当下,其实 alloc 和 allocWithZone 在底层是一样的。
首先来到if (fastpath(!cls->ISA()->hasCustomAWZ()))
,判断当前类是否没有自定义的 allocWithZone:
bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}
bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
这里的flag指的是cls结构体内部的class_rw_t的flags,而RW_HAS_DEFAULT_AWZ
则是用来标示当前的class或者是superclass是否有默认的alloc/allocWithZone:的,这个值存储在metaclass 中。
在第一次进入callAlloc方法时,flags的值为1,与上1<<16结果为0,返回的是 false,在hasCustomAWZ
这里取反,变为true,然后再在if (fastpath(!cls->ISA()->hasCustomAWZ()))
里取反,为false,于是不执行if当中的内容。
第二次进入callAlloc方法时,flags的值是一个很大的整数,与上1<<16结果为1,与上面恰好相反,于是执行if当中的内容。
首先是if (fastpath(cls->canAllocFast()))
:
bool canAllocFast() {
assert(!isFuture());
return bits.canAllocFast();
}
bool canAllocFast() {
return false;
}
可以看出,canAllocFast总是返回false,于是接下来是:
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
接下来是class_createInstance方法:
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
// 对 cls 进行判空操作
if (!cls) return nil;
// 断言 cls 是否实现了
assert(cls->isRealized());
// Read class's info bits all at once for performance
// cls 是否有 C++ 的初始化构造器
bool hasCxxCtor = cls->hasCxxCtor();
// cls 是否有 C++ 的析构器
bool hasCxxDtor = cls->hasCxxDtor();
// cls 是否可以分配 Nonpointer,如果是,即代表开启了内存优化
bool fast = cls->canAllocNonpointer();
// 这里传入的 extraBytes 为0,然后获取 cls 的实例内存大小
size_t size = cls->instanceSize(extraBytes);
// 这里 outAllocatedSize 是默认值 nil,跳过
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
// 这里 zone 传入的也是nil,而 fast 拿到的是 true,所以会进入这里的逻辑
if (!zone && fast) {
// 根据 size 开辟内存
obj = (id)calloc(1, size);
// 如果开辟失败,返回 nil
if (!obj) return nil;
// 将 cls 和是否有 C++ 析构器传入给 initInstanceIsa,实例化 isa
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
// 如果 zone 不为空,经过笔者测试,一般来说调用 alloc 不会来到这里,只有 allocWithZone
// 或 copyWithZone 会来到下面的逻辑
if (zone) {
// 根据给定的 zone 和 size 开辟内存
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
// 根据 size 开辟内存
obj = (id)calloc(1, size);
}
// 如果开辟失败,返回 nil
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
// 初始化 isa
obj->initIsa(cls);
}
// 如果有 C++ 初始化构造器和析构器,进行优化加速整个流程
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
// 返回最终的结果
return obj;
}
这就是alloc的全部流程了,下面是简略的流程图:
init简略分析
init 内部实现十分简单,先来到的是 _objc_rootInit ,然后就直接返回 obj 了。其实这里是一种抽象工厂设计模式的体现,对于 NSObject 自带的 init 方法来说,其实啥也没干,但是如果你继承于 NSObject 的话,就可以去重写 initWithXXX 之类的初始化方法来做一些初始化操作。
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
网友评论