这篇博客主要讲OC对象alloc的流程, 主要分两步: 第1:怎样探寻alloc方法的执行流程 第2:分析alloc函数执行流程
下面是OC对象alloc的流程图
一:怎样探寻alloc方法的执行流程
如上图所示,是一个OC对象alloc流程中所调用的一些方法。
- 我们可以直接通过xcode符号断点的方式,设置符号断点,结合xcode(按住command,点击step into 进入)
我们在想要查看的某个对象实例化的那行代码打上断点(由于程序运行起来会有大量的对象被创建,去除干扰,我们可以先将符号断点disable,当运行到断点时打开) 此时我们可以看见alloc内部调用了objc_alloc,接着我们急可以直接设置符号断点。拦截objc_alloc函数,查看内部调用的什么方法,以此类推
2.通过反汇编调试,查看调用链
选择 Always Show Disassembly,快速定位到objc_alloc结合符号断点,按住command,点击step into 进入,单步执行.
3.我们直接通过源码分析,我们通过上面的符号断点和反汇编调试,知道了objc_alloc是libobjc.A.dylib中的函数, 我们去apple open source下载源码编译,单步调试。
二:分析alloc函数执行流程
当实例化一个NSObject类的实例,反汇编+源码调试,会发现并没有执行NSObject的类方法alloc,而是直接执行的objc_alloc
1. alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
2. objc_alloc
id objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
callAlloc
static ALWAYS_INLINE id
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));
}
在callAlloc方法中,会发现会进入if (fastpath(!cls->ISA()->hasCustomAWZ()))
inline Class
objc_object::ISA(bool authenticated)
{
ASSERT(!isTaggedPointer());
return isa.getDecodedClass(authenticated);
}
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ (1<<14)
bool hasCustomAWZ() const {
return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
}
由此可以看出,当调用callAlloc,先通过当前cls的ISA 返回一个Class对象,然后判断当前cache中是否有缓存,如果有,执行_objc_rootAllocWithZone,如果没有,执行objc_msgSend, 继续运行程序, 会发现NSObject在cache中已经有缓存,直接执行了_objc_rootAllocWithZone
_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
static ALWAYS_INLINE id
_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;
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 {
// 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_rootAllocWithZone -> _class_createInstanceFromZone 创建一个类的实例,分三步:
1.cls->instanceSize 计算对象所需要的的内存大小
2.cls->calloc 申请开辟内存
3.cls->initInstanceIsa 通过isa,将对象于类关联
然后直接返回obj
添加一下自定义类型调用alloc
会发现p1实例在alloc执行流程中,cache中没有缓存,使用runtime objc_msgSend函数调用了alloc方法,然后再调用 _objc_rootAlloc -> callAlloc -> _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa 然后跳过p1, 会发现p2的alloc执行流程与上面的NSObject一样,就不会通过runtime的 objc_msgSend调用alloc方法,因为此时缓存中已经有LGPerson的缓存了,就直接 _objc_rootAllocWithZone执行后面的流程
总结
一个自定义类alloc的过程: 通过调用objc_alloc->callAlloc,在callAlloc内部分有缓存无缓存两种情况,当有缓存时通过 _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa 无缓存是,callAlloc内部通过objc_msgSend 调用alloc方法, 在alloc方法内部执行 _objc_rootAlloc -> callAlloc,后面步骤与上相同。