Objective - C 对象的本质(一) OC语言的本质及N
(一)OC语言的本质
- 其实我们编写的OC代码,底层实现都是C/C++代码
-
Objective-C的面向对象都是基于C\C++的数据结构实现的
语言转化流程
那么,是基于什么数据结构实现的呢?结构体
(1)将OC代码转换为C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
-
xcrun
Xcode run简写 -
-sdk iphoneos
指定SDK 运行的OS平台 -
clang
编译器的一种 -
-arch arm64
声明架构代码(i386 、armv7 、arm64) -
-rewrite-objc
重写objc代码 -
-o
输出 - 如果需要链接其他框架,使用-framework参数。如-framework UIKit
示例:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
(2)NSObject的本质
我们在.cpp文件中,找到这样一段代码,发现NSObject底层实现
struct NSObject_IMPL {//NSObject implementation
Class isa;//很重要,后面会讲到
};
另外我们在OC文件中,找到NSObject头文件的定义
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
通过这个我们可以侧面证明,NSObject的底层实现是通过C/C++的结构体的数据结构实现的
isa指针如果不好理解的话,isa指针可以类比数组的首元素,数组的首元素的地址也就是数组的首地址
(2)NSObject的占用的内存大小
我们继续查看isa,发现是这么定义的
typedef struct objc_class *Class;
这是一个指向结构体的指针
如下面的代码,我们通过runtime和malloc的方法得到NSObject所占用内存大小的信息,为什么会有8和16的区别呢?
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc] init];
//获得NSObject类的实例对象中成员变量所占内存的大小(内存对齐后)
NSLog(@"%zd",class_getInstanceSize([obj class]));//8
//获得obj指针所指向内存的大小
NSLog(@"%zu",malloc_size((__bridge const void *)obj));//16
}
return 0;
}
查看runtime源码找到class_getInstanceSize
方法
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
//再深入查看,发现这一条解释 返回的是成员变量占用的内存大小
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
而在NSObject调用alloc
方法实际上allocWithZone
方法,在runtime中最后调用的是_objc_rootAllocWithZone
方法,我们深入查看,最后找到instanceSize
方法,是指定对象内存大小的方法
NEVER_INLINE
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
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;//如果不足16个字节,强制分配16个字节
return size;
}
所以也就能解释,系统给NSobject对象分配16个字节,实际使用到的只有8个字节,分别通过class_getInstanceSize
函数和malloc_size
函数可以知道
(3)容易混淆的两个函数
class_getInstanceSize
-
malloc_size
上面用到了两个函数,很容易混淆他们之间的区别
-
创建一个实例对象,(内存对齐后)至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
-
创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
(3)窥探NSObject的内存
也可以使用LLDB指令(4)常用的LLDB指令
常用LLDB指令 读取内存方法不同 顺序会有区别(高高低低原则)具体窥探内存方法以及LLDB指令的使用在 Swift-七、枚举类型 可选项也有讲述