开发中经常使用 NSObject *object = [[NSObject alloc] init]; 这行代码去创建一个对象,那 alloc 和 init 分别干了些什么事情呢?
下方就是对 alloc 的一个初探过程,也会发现很多有意思的事情。
先上一张 alloc 的流程图。

然后我们还需要 objc-752 的源码,具体的配置过程参考 Cooci 大大的 这篇文章。
能运行 objc-752 源码后,在 main.m 中写入如下代码就开始了 alloc 的探索。
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *object = [[NSObject alloc] init];
NSLog(@"Hello, World! %@",object);
}
return 0;
}
commend 点击 alloc 就可以进入源码进行查看了。
注意:这里 alloc 点击进入的为 _objc_rootAlloc 其实我们断点后发现进入的是 objc_alloc。
1、alloc的第一个函数 objc_alloc 调用
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
当前传入了一个需要构建类的结构体 Class,是一个 objc_class 的类型,存储了一些当前类的信息。
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
//...
}
objc_class 又继承于 objc_class ,objc_class 存储了 isa 的一个 Class 结构体,指向当前是什么类。
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
2、进入 callAlloc 函数
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
/**
* bool(x) 为假的可能性更大 else 执行的概率会更大
* #define slowpath(x) (__builtin_expect(bool(x), 0))
*/
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
//这是判断一个类是否有自定义的 +allocWithZone 实现。
//hasCustomAWZ : hasCustomAllocWithZone
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
//没有实现就进入
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];
}
1、slowpath 的判断
第一句 if (slowpath(checkNil && !cls)) return nil; 其实就是判断了当前 cls 是否存在。
__builtin_expect 常用于 if-else 的判断为了优化判断的速度。
-
__builtin_expect(x,1)代表x为真的可能性更大,if下的代码执行的可能性更高 -
__builtin_expect(x,0)代表x为假的可能性更大,else下的代码执行的可能性更高
//bool(x) 为真的可能性更大
#define fastpath(x) (__builtin_expect(bool(x), 1))
//bool(x) 为假的可能性更大
#define slowpath(x) (__builtin_expect(bool(x), 0))
2、hasCustomAWZ 的判断
hasCustomAWZ 其实就是 hasCustomAllocWithZone 的意思。
if (fastpath(!cls->ISA()->hasCustomAWZ())) 这是判断一个类是否有自定义的 +allocWithZone 实现。
3、canAllocFast 的判断
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;
}
这里的大概意思是:没有类,没有 isa 指针才会进入,这里有点不太明白。
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;
}
else 就进入了创建过程了。
4、class_createInstance 函数的调用
函数原型可以看到,传入了当前类的Class 和一个 extraBytes 额外需要的内存大小 0。
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
3、进入 _class_createInstanceFromZone 函数
_class_createInstanceFromZone 是 alloc 的所有执行操作了,包括类大小的获取,类的堆空间开辟,isa 指针的指向。
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
//hasCxxCtor() 是判断当前 class 或者 superclass 是否有 .cxx_construct 构造方法的实现。
bool hasCxxCtor = cls->hasCxxCtor();
//hasCxxDtor() 是判断判断当前 class 或者 superclass 是否有 .cxx_destruct 析构方法的实现。
bool hasCxxDtor = cls->hasCxxDtor();
//具体标记某个类是否支持优化的isa.
bool fast = cls->canAllocNonpointer();
//获取类的大小 (传入额外字节的大小)
size_t size = cls->instanceSize(extraBytes);
//如果传入分配大小就需要修改
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
/**
* void *calloc(size_t __count, size_t __size)
* 在内存的动态存储区中分配 __count 个长度为 __size 的连续空间
*/
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
static __attribute__ 的标识代表 OC 中 C++ 的全局构造函数。
bool hasCxxCtor = cls->hasCxxCtor(); hasCxxCtor() 是判断当前 class 或者 superclass是否有 .cxx_construct 构造方法的实现。
bool hasCxxDtor = cls->hasCxxDtor(); hasCxxDtor() 是判断判断当前 class 或者 superclass 是否有 .cxx_destruct 析构方法的实现。
bool fast = cls->canAllocNonpointer(); 具体标记某个类是否支持优化的isa.
bool canAllocNonpointer() {
assert(!isFuture());
//实例是否需要原始Isa instancesRequireRawIsa = NO
return !instancesRequireRawIsa();//YES
}
size_t size = cls->instanceSize(extraBytes); 获取类的大小(传入额外字节的大小)
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
//CF 最小16字节
if (size < 16) size = 16;
return size;
}
在 unalignedInstanceSize 方法中 data()->ro->instanceSize; 获取类所占用空间的大小,其实是在 MacO 的 data 段的 ro 中的获取类所占用的大小。
这里有一些有意思的事情,比如 字节对齐,CoreFoundation 规定字节数最小为 16。
关于字节对齐:OC 是 8 字节对齐,在 word_align 这个方法中计算了字节对齐。
static inline uint32_t word_align(uint32_t x) {
//字节对齐
return (x + WORD_MASK) & ~WORD_MASK;
}
计算过程如下:
假如: x = 9
x + WORD_MASK = 9 + 7 = 16
WORD_MASK 二进制 :0000 0111 = 7 (4+2+1)
~WORD_MASK : 1111 1000
16二进制为 : 0001 0000
1111 1000
0001 0000
---------------
0001 0000 = 16
所以 x = 16(原始值:9) 也就是 8的倍数对齐,即 8 字节对齐
上方的计算过程就能看到 OC 采用了 8 字节对齐。
if (outAllocatedSize) *outAllocatedSize = size; 如果传入分配大小就需要使用传入的,但是上方默认值为 nil ( size_t *outAllocatedSize = nil )。
然后调用 void *calloc(size_t __count, size_t __size) 在内存的动态存储区中分配 __count 个长度为 __size 的连续空间。
获取到了 obj 然后 使用 obj->initInstanceIsa(cls, hasCxxDtor); 绑定 isa 指针,说明这块内存空间是为谁开辟的。
最后返回 obj。
这就是一个对象的初始化过程。
咦,那 init 到底干嘛了??
其实 init 什么也没有干,就是为了提供一个自定义接口,方便开发者自己实现一些在初始化需要干的事情。
// Replaced by CF (throws an NSException)
+ (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;
}
以上就是对 alloc 的初探 的内容了。