示例代码:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
思考1
一个 NSObject
对象会占用多少内存空间
思考2
alloc
、init
分别进行了什么操作
分析
我们都知道 OC 是基于 C/C++ 语言来实现的,所以我们可以将我们的 OC 代码转成 cpp 文件来查看一下。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o mainarm64.cpp
运行成功之后会生成下面文件
image.png在这个文件的最后我们可以找到我们的 main方法 和 NSObject 的定义
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
/*
NSObject *obj = objc_msgSend(objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
*/
}
return 0;
}
struct NSObject_IMPL {
Class isa;
};
源码分析
我们可以看到在NSObject.mm
中如下定义:
可以看出,所有的创建方法都会触发 callAlloc
函数,而我们 init
只不过是在 callAlloc
之后调用了 init
方法
然后会在 callAlloc
方法中进入 _objc_rootAllocWithZone
方法
然后会调用 _class_createInstanceFromZone
方法
而 size = cls->instanceSize(extraBytes);
这个方法就是为我们计算需要多少内存的方法
hasFastInstanceSize的作用就是判断能否快速查找需要开辟的内存空间大小(查找是否有缓存数据) 当无法进行快速查找的时候就会使用 alignedInstanceSize
进行计算,可以看到里面当 size 小于 16 时,size 是强制等于 16 的,所以我们可以认为我们最小对象计算的空间这里给的一定是 16
__builtin_constant_p 是GCC的内建函数 用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0
此处计算出的值为 16 且会直接进入到 align16
的方法。
_flags是chass_rw_t结构体中的属性,在初始化alloc方法时系统回调用objc_class结构体中的setInfo方法,并调用data->setFlags 方法为_flags进行了赋值
x+size_t(15) & ~size_t(15): 将x与15相加,然后再和15按位取反的数进行按位与计算,示例如下, 令 x = 7:
x = 7 0000 0111
15 0000 1111
x + 15 0001 0110
~15 1111 0000
x + size_t(15) & ~size_t(15)
16 0001 0000
image.png
可以看出该算法是开辟的内存使用实际开辟的内存进行16的倍数扩容。
当拿到计算出的内存空间时,然后进入 _class_createInstanceFromZone
中的 calloc
进行分配内存。
然后调用 initInstanceIsa
进行 ISA 绑定
然后将构建好的 obj 进行返回。
所以在此处我们可以确认我们的 NSObject 对象是分配了 16 个字节的内存。
然后我们来看 init
操作,点击 init 会进入到下面方法
所以我们可以看到 init 其实返回了对象本身,并没有任何操作。
最后可以验证一下
image.png
网友评论