一 本质
OC 的面向对象都是基于C/C++ 的数据结构实现的
1 OC 的对象和类主要是基于C/C++ 什么数据结构实现的?
结构体
2 clang 命令转换成c++ 代码
clang -rewrite-objc main.m -o main.cpp
以上的命令是不分平台进行编译的,main.cpp 代码有9万多行,如果指定用iphone 的话,用以下的命令,编译之后有3万多行代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
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"));
}
return 0;
}
4 NSObject 的本质
在c++代码中这是NSObject的实现
NSObject_IMPL 就是意味着 NSObject 的 Implementation
struct NSObject_IMPL {
Class isa;
};
二 一个NSObject占用多少内存
1 按道理来讲,一个NSObject 本质就是一个结构体,其中的一个isa 指针在64位中是占据8个字节的
实际是16个字节的
NSObject *obj = [[NSObject alloc]init];
利用runtime 中的 class_getInstanceSize 获取大小
// 以下打印结果是8 ,以下代码的含义就是获取一个类的实例对象的大小
NSLog(@"%zu",class_getInstanceSize([NSObject class]));
导入 #import <malloc/malloc.h>
// 打印结果是 16,以下代码的含义是获取这个指针所指向内存的大小
NSLog(@"%zu",malloc_size((__bridge const void *)(obj)
所以这样来讲,如果问一个NSObject 对象占用多少内存,或者分配多少内存,就是16个字节
如果问 一个NSObject 对象实际是多大的内存,就是8个字节
2 为什么呢 ?
首先我们打开苹果开源的代码,找到代码一步步分析,找到最新的objc 代码下载分析
https://opensource.apple.com/tarballs/objc4/
打开下载的工程文件 搜索allocWithZone(alloc本质调用的还是这个方法)
以下是源代码调用的顺序
源码调用顺序
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
if (fastpath(!zone)) {
obj = class_createInstance(cls, 0);
} else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
return obj;
}
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
void *bytes;
size_t size;
// Can't create something for nothing
if (!cls) return nil;
// Allocate and initialize
size = cls->alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
if (zone) {
bytes = malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
bytes = calloc(1, size);
}
return objc_constructInstance(cls, bytes);
}
// 内部有一个方法 instanceSize, 当内存不足16的时候,直接返回16
inline 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;
}
函数调用顺序。allocWithZone - > objc_rootAllocWithZone -> class_createInstanceFromZone -> instanceSize(内部分配内存的方法)
说结论吧,内部调用了一个方法 instanceSize 方法 ,内存不足16,直接返回16 。
至于instance 方法内部又去调用了alignedInstanceSize 方法,这个方法其实返回的就是8,为什么呢? 因为isa 指针的大小就是8,说的很明白就是成员变量的大小
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
3 如何在内存中看呢?
获取 obj 的内存地址
在控制台中 打印
memory read 0x100664d00 这个地址,然后就是打印出,也是连续的16个字节
0x100664d00: 19 c1 02 95 ff ff 1d 00 00 00 00 00 00 00 00 00
用以下明令也是可以的
x 0x100664d00