浅谈iOS对象的本质和神秘的nonPointerIsa

对象的本质

探讨对象的本质前,我们先了解一下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文件,转化后的文件部分代码截图如下:
本质1.png
对此底层代码进行分析:发现对象OCPeople在底层被编译成了结构体。结构体OCPeople_IMPL内部还嵌套了一个结构体NSObject_IMPL NSObject_IVARS,是因为OCPeople继承自NSObject

NSObject_IVARS是成员变量isa,如下图:
isa.png

值得注意这里有一句:typedef struct objc_object OCPeople;为什么OCPeople类本质的类型是objc_object这个类型呢?是因为OCPeople继承自NSObjectNSObject在底层的本质是objc_object。那class是什么类型呢?查找发现它是objc_class *,它是结构体类型的指针,如下图:
class.png
为啥我们常用到的id可以指向任意对象且不用带*,是因为id本来就是objc_object *类型的结构体指针,在oc开发中,基本上所有的对象都是继承自NSObject,而它的本质是objc_object结构体类型。

底层代码main.cpp中有这样一段代码,如下图:
getset.png
这段代码是属性nameageget方法和set方法。从底层代码中可以看到系统自动给这两个属性添加了get方法和set方法。而我们定义的变量nickName,系统却没有自动添加get方法和set方法,这个要注意区分。OCPeople * self, SEL _cmd是底层的两个隐藏参数。((char *)self + OBJC_IVAR_$_OCPeople$_name)可以这样理解:OCPeople对象在堆区开辟内存空间,堆区空间存放isanameage等等一些变量,对象的首地址加上OBJC_IVAR_$_OCPeople$_name所在内存空间的偏移量才能获得name的地址,拿到地址才能获取name里面的值。

总结:对象的本质是结构体objc_object,对象都包含一个class类型的isa是继承自NSObjectclass的本质是结构体类型指针objc_class *

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享