生成C++文件的一些命令:
clang -rewrite-objc main.m -o main.cpp:(无法区分平台 不建议使用)
xcrun -sdk iphonesimulator clang -rewrite-objc main.m -o main.cpp:(模拟器)
xcrun -sdk iphoneos clang -rewrite-objc main.m -o main.cpp:(真机)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 源文件名 -o 输出的cpp文件名:(arm64架构 最常用)
当把生成的C++文件放到Xcode之后,当然是不能直接运行了,这时候点击左侧栏的项目,然后再在右边的Build Phases里找到Compile Sources把不需要运行的C++源代码文件删掉,编译时就会自动忽略咯。
NSObject *objc = [[NSObject alloc] init];
底层实现:
NSObject
的底层是一个结构体类型,内部有一个Class
类型的成员名叫isa
:
//impl就是implementaion实现
struct NSObject_IMPL {
Class isa;
};
那么isa
是什么?
右键点击jump to definition,可以找到:
typedef struct objc_class *Class;
那么我们可以知道,isa是一个指针,那么在64位下占8位内存,32位机下就占4位内存。
我们可以通过.h文件看到,NSObject
内部确实只有一个Class
类型的isa
成员,其余皆是一些方法。
若我们了解内存分配就知道,我们探索一个类的内部结构的内存布局情况,关注其成员对象即可,不需要关注其方法(方法存储在公共内存位置,提供给所有的实例对象使用、类对象使用)。
因此,我们进一步去看看Class
是什么东西即可:
我们查看源码得知,Class
本质是一个 objc_class
类型的结构体指针
我们进一步去看一下 objc_class
这个结构体:
我们发现objc_class
结构体继承自objc_object
结构体,且objc_class
内部有若干成员如下(忽略其函数、方法):
struct objc_class : objc_object {
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
....
}
我们跳进去查看 objc_object
发现,其本身也只有一个 isa_t
类型的成员
struct objc_object {
private:
isa_t isa;
最终 isa_t
类型的成员是一个联合体:
通过前面的介绍,我们可以将NSObject的定义简写为:
目前官方暴露的头文件中的格式:
@interface NSObject <NSObject> {
Class isa ;
}
@end
简写格式:
@interface NSObject <NSObject> {
objc_class isa ;
}
@end
结构体objc_class的实现:
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
....
}
OC对象的本质
Class
的isa
的成员变量NSObject_IMPL
的结构体isa
本质上也是一个指向objc_class
结构体的指针(objc_class
继承自objc_object
结构体,内部有一个isa
成员)我们从打印结果中可以得出结论:一个NSObject对象,系统给其分配了16个字节
101323a20
与101323a4F
之间差48,刚好显示差了一行101323a20
的整个存储空间为绿色框框出来的一部分,蓝色框为101323a30
开始的部分了NSObject
中有十六个字节,但是只用了八个字节来存储内容。与前面的方式是得到的结论是相符合的。我们通过阅读苹果官方开源的源码,我们可以看到runtime中有一个函数:
/**
* Returns the size of instances of a class.
*
* **@param** cls A class object.
*
* **@return** The size in bytes of instances of the class \e *cls,* or \c 0 if \e *cls* is \c Nil.
*/
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
我们通过调用可以看到结果:
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
结果是8。
为什么是8??跟我们前面得到的结论不一致!!!我们继续往下探索!!
我们知道,OC中创建对象分配内存是通过 alloc
方法,我们直接去看官方开源的程序中alloc
的实现即可(本质上最终alloc
的实现就会调用allocWithZone:
方法):
OBJC_SWIFT_UNAVAILABLE
这段宏是限制Swift语言分配内存(因为目前存在OC+Swift混编的情况,这个不是本篇幅谈论的范畴,咱们只关注 纯OC 环境即可)
我们从NSObject.mm
文件可以看到其实现调用的函数:
从图上我们清晰看见,最终其调用的是objc里面的 _objc_rootAllocWithZone
函数:
我们通过全局搜索找到函数的内部实现:
我们可以看到语言逻辑,在其首次分配内存的时候,调用了函数: class_createInstance class_createInstance
函数最终也是调用了 class_createInstanceFromZone
函数
obj = class_createInstance(cls, 0);
从函数的实现中我们也看到了,CF框架要求:所有对象至少分配16个字节内存。这涉及到内存对齐的概念。
isa
指针的,我们来验证一下:如图所示,我们无法直接访问isa
私有成员变量(苹果设计不可以直接访问),但是我们窥探过其开源代码,知道其数据结构,我们可以自己在外部写一个类似的结构体对象进行调试打印!且通过验证得出结论,我们的猜想是正确的!
那么我们可以得出结论:class_getInstanceSize
这个runtime
函数是用来获取,创建一个对象的实例,至少得给其分配多少内存的!(也就是其本身的数据结构需要多少内存)
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
我们进一步去找一找源码,看看系统底层对内存对齐方面的处理
:
我们可以看到红框框中框出来的部分,为分配内存的代码!在objc这份源码中已经看不到了,我们要重新去苹果的OpenSource去下载libmalloc这个开源文件进行探索: 我们查找到 _malloc_zone_calloc
的实现:
从上图,我们不难得知,本质上还是调了calloc
函数
其中zone->calloc
传入的zone
就是 上一步中的 default_zone
这个关键代码的目的就是申请一个指针,并将指针地址返回 calloc
这个函数的实现,苹果官方没有开源
但是我们在系统暴露的malloc.h
头文件中找到了一个函数:
extern size_t malloc_size(const void *ptr); /* Returns size of given ptr */
其解释是指,创建对象时,分配多少内存,并把分配的内存地址返回给指针ptr
。我们试着用一下这个函数去获取一下实际分配的内存:
从打印结果我们可以看到 实际分配了16个字节,与前面的几种方式得到的结论一致!!
创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
创建一个NSObject对象:
isa
指针随手写两个类:
//
// main.m
// 窥探iOS底层原理
//
// Created by VanZhang on 2022/5/6.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
struct {
Class isa;
}NSObject_ISA;
@interface Car :NSObject{
@public
int _year;//多少年了 4个字节
int _kilometres;//多少公里数 4个字节
}//int 4+ int 4+ isa 8 = 24 ;需要24
//内存对齐参数为16 ;总共分配了能容纳其需要的16的最小公倍数:32
-(void)run;
@end
@implementation Car
- (instancetype)init{
self = [super init];
if (self) {
_year = 1;
_kilometres = 2;
}
return self;
}
- (void)run{
NSLog(@"%s",__func__);
}
@end
@interface BBA_BMW :Car{
@public
NSString*_nameplate;//汽车铭牌 8个字节
}//int 8+ double 8+ isa 8 + _nameplate 8= 32 ;需要32
//内存对齐参数为16 ;总共分配了能容纳其需要的16的最小公倍数:32
-(void)runFaster;
@end
@implementation BBA_BMW
- (void)runFaster{
NSLog(@"%s",__func__);
}
@end
void testFunc(void){
Car *c = [[Car alloc]init];
c->_year = 18;
c->_kilometres = 890123;
NSLog(@"Car_class_getInstanceSize:%zd",class_getInstanceSize([c class]));
NSLog(@"Car_size:%zd",malloc_size((__bridge const void *)(c)));
BBA_BMW *bba = [[BBA_BMW alloc]init];
bba->_nameplate = @"宝马七系";
NSLog(@"BBA_BMW_class_getInstanceSize:%zd",class_getInstanceSize([bba class]));
NSLog(@"BBA_BMW_size:%zd",malloc_size((__bridge const void *)(bba)));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc]init];
// NSLog(@"%@",obj);
NSLog(@"class_getInstanceSize:%zd",class_getInstanceSize([obj class]));
// NSLog(@"isa:%zd",sizeof(obj->isa));
NSLog(@"isa:%zd",sizeof(NSObject_ISA));
NSLog(@"malloc_size:%zd",malloc_size((__bridge const void *)(obj)));
testFunc();
}
return 0;
}
car:
我们前面将_year
设置为18,在16进制中,0x12=十进制的18
实际分配了32,真正用到了24
bba:
我们前面将_year
默认设置为1,将 _kilometres
改为int
类型 默认设置为 2
内存分布情况如下:
OC对象主要分为三种:
通过OC语言编写的程序中,instance
对象就是通过类alloc
出来的对象,每次调用alloc
都会产生新的instance
对象。
在上文中,通过讨论 NSObject对象的底层实现、内存布局、通过继承关系了解 子类、孙子类的 内存布局
…就是探索instance
对象 的底层过程的开端。
Class对象其实是一个指向objc_class结构体的指针。因此我们可以说类对象或元类对象在内存中其实就是objc_class结构体。
我们在探索NSObject对象的时候,看到了底层代码:
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
....
}
结合 NSObject.h
文件:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
...
}
从前面两段代码,我们不难得知基类NSObject
中有一个Class
类型的对象:isa
指针
我们把前面创建了四个类,且有继承关系的代码,通过clang
指令转换成c++
:
结合前面的继承关系,和转换成c++底层代码之后的具体情况(如上图),我们不难得出一个结论:
每个类在内存中有且只有一个class
对象
还可以通过打印内存地址证明:
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
// 通过打印可以看出,上面几个方法返回的都是同一个类对象,内存地址都一样
NSLog(@"class - %p %p %p %p %p %d",objectClass1,objectClass2,objectClass3,objectClass4,objectClass5;
我们可以通过NSObject的类方法
、实例方法
获得 类对象
:
我们还可以通过 Runtime的API获得 类对象
:
Class _Nullable object_getClass(id _Nullable obj)
该API效力等同于+ (Class)class、- (Class)class
注意: class方法返回的一直是类对象,所以哪怕这样写还是会返回类对象
Class objectMetaClass2 = [[[NSObject class] class] class];
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
//struct old_class_ext *ext;
uint32_t size;
const uint8_t *weak_ivar_layout;
struct old_property_list **propertyLists;
....
}
查看源码之后,我们不难得出结论,class对象在内存中存储的信息
:
isa
指针superclass
指针@property
)、类的对象方法信息(instance method
)我们通过runtimeAPI
遍历一下methodLists
内部的信息,得知:struct old_method_list **methodLists
内部存储的都是 类的对象方法信息(instance method
)
类的协议信息(protocol
)、类的成员变量信息(ivar
)
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:
对instance对象和Class对象的总结:
isa指针: 每个类 在内存中 有且只有一个 Class对象,isa指针
class对象在内存中存储的信息:
成员变量的值 存储在实例对象中
: 是存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值
成员对象的类型、值、名称,存储在在class对象中
: 但是 成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中
通过前面篇幅的介绍,我们得知:每个类的实例对象、类对象、元类对象
都有一个isa
指针
void test11(void){
Class animalMetaCls = getMetaClassFromClass([BBA_BMW_RunFaster class]);
NSLog(@"superclass:%@",[BBA_BMW_RunFaster superclass]);
NSLog(@"superclass-superclass:%@",[[BBA_BMW_RunFaster superclass] superclass]);
NSLog(@"superclass-superclass-superclass:%@",[[[BBA_BMW_RunFaster superclass] superclass] superclass]);
NSLog(@"superclass-superclass-superclass-superclass:%@",[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass]);
NSLog(@"superclass-superclass-superclass-superclass-superclass:%@",[[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass] superclass]);
NSLog(@"====================");
NSLog(@"metaClass:%@",animalMetaCls);
NSLog(@"metaClass-superclass:%@======metaClass-superclass_class_isMetaClass:%d",[animalMetaCls superclass],class_isMetaClass([animalMetaCls superclass]));
NSLog(@"metaClass-superclass-superclass:%@======metaClass-superclass-superclass_class_isMetaClass:%d",[[animalMetaCls superclass]superclass],class_isMetaClass([[animalMetaCls superclass]superclass]));
NSLog(@"====================");
NSLog(@"metaClass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclass_class_isMetaClass:%d",[[[animalMetaCls superclass]superclass]superclass],class_isMetaClass([[[animalMetaCls superclass]superclass]superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[animalMetaCls superclass]superclass]superclass]superclass],class_isMetaClass([[[[animalMetaCls superclass]superclass]superclass]superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass],class_isMetaClass([[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass--superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass],class_isMetaClass([[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass]));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test11();
}
return 0;
}
通过前面篇幅的介绍,我们得知:每个类的类对象、元类对象都有一个superclass指针
class
的superclass
指针指向父类的class
superclass
指针为nil
meta-class
的superclass
指向父类的meta-class
meta-class
的superclass
指向基类的class
参考博客:
07-探究iOS底层原理|几种OC对象【实例对象、类对象、元类】、对象的isa指针、superclass、对象的方法调用、Class的底层本质
06-探究iOS底层原理|OC对象的本质【底层实现、内存布局、继承关系】