OC对象alloc流程探索
alloc方法
我们每天都在创建对象,今天就来看看alloc到底做了些什么。
NSObject *obj = [NSObject alloc];
NSObject *obj1 = [[NSObject alloc] init];
我们在 xcode 工具栏 选择 Debug --> Debug Workflow --> Always Show Disassembly, 显示反汇编。
可以看到alloc其实直接调用了objc_alloc,让我们通过源码来看一下alloc的流程。
alloc流程
objc_alloc

callAlloc
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
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));
}
slowpath 与fastpath
宏定义如下:
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
__builtin_expect
__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
这个指令的写法为: __builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
fastpath:
(__builtin_expect(bool(x), 1)),表示 x 的值为真的可能性更大;即执行 if 里面语句的机会更大。
slowpath:
(__builtin_expect(bool(x), 0)),表示 x 的值为假的可能性更大,即执行 else 里面语句的机会更大。
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
没有自定义allocWithZone,则会走到_objc_rootAllocWithZone
_objc_rootAllocWithZone

_class_createInstanceFromZone (核心代码)
核心代码:
1.cls->instanceSize:计算需要开辟的内存大小
2.calloc:申请内存,返回地址指针
3.obj->initInstanceIsa:将类与isa关联
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);
}
cls->instanceSize
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
计算每个属性的大小,这里有个重点就是字节对齐。
calloc:申请内存,返回地址指针
initInstanceIsa
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
绑定isa后,打印出的obj就是NSObject类型了。具体isa是如何关联的我们后面再分析。

init 和 new
init
+ (id)init {
return (id)self;
}
- (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;
}
我们发现init的类方法和对象方法返回的都是id对象本身。
不同的是类方法返回了一个id类型的self,这是为了可以给开发者提供自定义构造方法的入口,通过id强转类型实现工厂设计,返回我们定义的类型。
new
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
实际上就是完成调用了alloc+init。
唯一区别:
采用[alloc init]方式可以灵活拓展,比如实现initWithXXX这种自定义的init方法,而采用new方式只能重写父类的init方法。
总结
alloc核心流程:
1.计算需要申请的内存空间大小。
2.为对象分配内存空间,并返回内存地址。
3.将类和开辟的内存空间关联起来。