开发中经常使用 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 的初探
的内容了。