This blog mainly talks about the process of OC object alloc, which is mainly divided into two steps: 1: How to explore the execution process of the alloc method 2: Analyze the execution process of the alloc function
The following is the flow chart of the OC object alloc
1: How to explore the execution flow of the alloc method
As shown in the figure above, it is some methods called in the alloc process of an OC object.
- We can set symbolic breakpoints directly through xcode symbolic breakpoints, combined with xcode (press and hold command, click step into to enter)
We set a breakpoint on the line of code where an object instantiation is to be viewed (because a large number of objects will be created when the program is running, to remove interference, we can disable the symbolic breakpoint first, and open it when the breakpoint is reached. ) At this point, we can see that objc_alloc is called internally by alloc , and then we can set a symbolic breakpoint directly. Intercept the objc_alloc function to see what method is called internally, and so on
2. View the call chain through disassembly debugging
Select Always Show Disassembly , quickly locate the objc_alloc combined symbol breakpoint, hold down command, click step into to enter, and step into it.
3. We directly analyze the source code. Through the above symbolic breakpoints and disassembly debugging, we know that objc_alloc is a function in libobjc.A.dylib . We go to apple open source to download the source code to compile and single-step debugging.
Two: Analyze the execution flow of the alloc function
当实例化一个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执行后面的流程
总结
The process of a custom class alloc: By calling objc_alloc->callAlloc , there are two cases of cache and no cache in callAlloc. When there is cache, pass _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa no cache, callAlloc The alloc method is called internally through objc_msgSend , and _objc_rootAlloc -> callAlloc is executed inside the alloc method. The following steps are the same as above.