对象的本质
探讨对象的本质前,我们先了解一下clang
编译器:clang
是一个c++
编写、基于llvm
、发布于llvm bsd许可证下的c/c++/objective-c/objective-c++
的轻量级编译器。
来一段main.m
代码如下:
#import <Foundation/Foundation.h>
@interface OCPeople : NSObject{
NSString *nickName;
}
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@end
@implementation OCPeople
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
这里介绍一个clang
命令:clang -rewrite-objc main.m -o main.cpp
,这条命令是将main.m
文件转化为main.cpp
文件。
在终端输入上面的命令,main.m
所在的文件夹內就会生成一个main.cpp
文件,转化后的文件部分代码截图如下:
对此底层代码进行分析:发现对象OCPeople
在底层被编译成了结构体。结构体OCPeople_IMPL
内部还嵌套了一个结构体NSObject_IMPL NSObject_IVARS
,是因为OCPeople
继承自NSObject
。
NSObject_IVARS
是成员变量isa,如下图:
值得注意这里有一句:typedef struct objc_object OCPeople;
,为什么OCPeople
类本质的类型是objc_object
这个类型呢?是因为OCPeople
继承自NSObject
,NSObject
在底层的本质是objc_object
。那class
是什么类型呢?查找发现它是objc_class *
,它是结构体类型的指针,如下图:
为啥我们常用到的id
可以指向任意对象且不用带*
,是因为id
本来就是objc_object *
类型的结构体指针,在oc开发中,基本上所有的对象都是继承自NSObject
,而它的本质是objc_object
结构体类型。
底层代码main.cpp
中有这样一段代码,如下图:
这段代码是属性name
和age
的get
方法和set
方法。从底层代码中可以看到系统自动给这两个属性添加了get
方法和set
方法。而我们定义的变量nickName
,系统却没有自动添加get
方法和set
方法,这个要注意区分。OCPeople * self, SEL _cmd
是底层的两个隐藏参数。((char *)self + OBJC_IVAR_$_OCPeople$_name)
可以这样理解:OCPeople
对象在堆区开辟内存空间,堆区空间存放isa
,name
,age
等等一些变量,对象的首地址加上OBJC_IVAR_$_OCPeople$_name
所在内存空间的偏移量才能获得name
的地址,拿到地址才能获取name里面的值。
总结:对象的本质是结构体objc_object
,对象都包含一个class
类型的isa
是继承自NSObject
,class
的本质是结构体类型指针objc_class *
。