[iOS]类和对象的底层探索
有些部门前面讲的不是很清晰
或者没讲的很细致的
在这一篇重新拿出来记录一下
也当作是复习一遍
继承链(类,父类,元类)
先来结论
instance 实例对象
instance对象就是通过alloc方法创建出来的对象,每次调用alloc方法都会生成新的instance对象
instance对象在内存中存放的信息包括
class 类对象
class对象的作用是用来形貌一个instance对象,它内部存放一个类的属性信息(@property)、对象方法信息(instance method)、协议信息(protocol)、成员变量信息(ivar),别的class对象内里另有两个指针,isa指针 和 superclass指针。
Objective-C类是由Class类型来表示的,它现实上是一个指向objc_class结构体的指针
类对象就是一个结构体struct objc_class,这个结构体存放的数据称为元数据(metadata),
该结构体的第一个成员变量也是isa指针,这就说明确Class本身其实也是一个对象,因此我们称之为类对象
类对象在编译期产生用于创建实例对象,是单例
meta-class 元类对象
meta-class对象的作用是用来形貌一个class对象
跟class一样,元类对象在内存中也是只有一份的
它内部存储了 isa指针 + superclass指针 + 类方法信息(+方法)
类对象中的元数据存储的都是怎样创建一个实例的相关信息
那么类对象和类方法应该从哪里创建呢? 就是从isa指针指向的结构体创建
类对象的isa指针指向的我们称之为元类(metaclass), 元类中生存了创建类对象以及类方法所需的全部信息
来看这张很经典的图
通过上图我们可以看出整个体系构成了一个自闭环,struct objc_object结构体实例它的isa指针指向类对象
类对象的isa指针指向了元类,super_class指针指向了父类的类对象
而元类的super_class指针指向了父类的元类,那元类的isa指针又指向了本身
为什么要有元类?
元类(Meta Class)是一个类对象的类。在上面我们提到,全部的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念,元类中生存了创建类对象以及类方法所需的全部信息。任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为本身的所属类,而基类的meta-class的isa指针是指向它本身
OC的类其实也是一个对象
一个对象就要有一个它属于的类
意味着类也要有一个isa指针指向其所属的类
那么类的类是什么
就是我们所说的元类(MetaClass)
以是,元类就是类的所属类
既然元类是个类那元类的类是什么呢?
全部的元类都使用根元类作为他们的类
根元类的 isa 指针指向了它本身
对对象、类、元类和分类的探索
法一
可以打开前面提到过的runtime源码查看相关定义
法二
将OC代码转换为C/C++代码之后再对其举行研究
法二可行的理由是因为OC本质底层实现转化其实都是C/C++代码
咱们今天先通过法二举行探索 同时学习一下转化OC代码的操作
使用上面指令可以转化OC代码
但是如果你的代码内引用了UIKit之类的库
那么会出现这么一条报错
好 怎么办呢
可以通过加一些参数指定使用 iOS 体系的 SDK
- gcc -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
复制代码 也可以通过xcrun工具
Xcode安装的时候顺带安装了xcrun下令,xcrun下令在clang的底子上进⾏了⼀些封装,要更好⽤⼀些
- xcrun -sdk iphoneos gcc -rewrite-objc main.m
复制代码 instance 实例对象
对于我们的MYObject对象
找到它的对应部门
typedef struct objc_object MYObject;
这里使用 typedef 为 struct objc_object 结构体定义了一个别名 MYObject
以是对象本质上就是结构体
typedef struct {} _objc_exc_MYObject;
同样是使用 typedef 定义了一个名为 _objc_exc_MYObject 的结构体类型,不外这里的结构体没有具体的成员。
struct MYObject_IMPL {...};
这是定义了一个名为 MYObject_IMPL 的结构体。该结构体包含了另一个结构体 NSObject_IMPL NSObject_IVARS,以及两个 NSString * 类型的成员变量 _age 和 _Nonnull _name
“IMPL”常见的是“implementation”的缩写,意思是“实现”。在编程中,它通常用于表示某个类、方法或功能的具体实现部门。例如,大概会有一个接口(interface)定义了一些方法,而“IMPL”后缀的类则是具体实现这些方法的类。
class 类对象
我们接着找到NSObject_IMPL的定义部门
关于NSObject的具体实现部门只有一个Class类型的isa指针
那么再看看Class类型到底是何方神圣
可以看出Class底层是objc_class *类型
咱们前面讲过objc_class结构体本质上代表了类对象
由此接纳第一个结论
类对象中的元数据存储的都是怎样创建一个实例的相关信息
同时发现
- id底层实现是struct objc_object *类型,定义为了一个Class的指针。
- SEL是struct objc_selector *类型
- IMP是void (*)(void)函数指针类型
接下来我们要找objc_class *的定义
但是在这个转化的文件内 找不到objc_class *的定义了
好 开始挑源码来看
定义了洋洋洒洒五百行
方法存储在公共内存位置,提供给全部的实例对象使用、类对象使用
以是咱除去方法,主要看看其成员变量
- struct objc_class : objc_object {
- // Class ISA;
- Class superclass;
- cache_t cache; // formerly cache pointer and vtable
- class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
- }
复制代码 好 大概和大家在其他博客内看到的不太一样
上面是在2006年苹果发布Objc 2.0之后,objc_class的定义
之前的长如许
- struct objc_class {
- Class isa OBJC_ISA_AVAILABILITY;
-
- #if !__OBJC2__
- Class super_class OBJC2_UNAVAILABLE;
- const char *name OBJC2_UNAVAILABLE;
- long version OBJC2_UNAVAILABLE;
- long info OBJC2_UNAVAILABLE;
- long instance_size OBJC2_UNAVAILABLE;
- struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
- struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
- struct objc_cache *cache OBJC2_UNAVAILABLE;
- struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
- #endif
-
- } OBJC2_UNAVAILABLE;
复制代码 分析一下
superclass 指针指向该类的父类,通过它可以建立类的继承关系。
cache(从前是缓存指针和虚函数表)用于存储方法等的缓存信息,以提高方法查找速率。
bits 通过位运算等方式存储和管理了一些相关信息及自定义标记,例如类的读写权限、是否有自定义的内存分配标记等。
虽然这些成员变量本身并不直接包含全部的类信息,但通过它们以及与之相关的各种方法和运行机遇制,可以访问到类的其他具体信息,如属性、方法列表、协议列表等。这些信息大概通过间接的方式存储、盘算或在运行时动态获取。
将源码的定义转化为类图
NSObject本质上是一个叫做NSObject_IMPL的结构体
其成员变量isa本质上也是一个指向objc_class结构体的指针(objc_class继承自objc_object结构体,内部有一个isa成员)
meta-class 元类对象
其实元类和类的本质都是objc_class结构体,只不外它们的用途不一样,类的methods成员变量里存储着该类全部的实例方法信息,而元类的methods成员变量里存储着该类全部的类方法信息
分类(category)
分类是OC的一个高级特性,我们一样平常用它给体系的类或者第三方库的类扩展方法,属性和协议,或者把一个类不同功能分散到不同的模块中实现
来看看它的源码
同样属于结构体
说法一
可以看到内里是没有成员变量列表存在的,如允许以表明分类为什么不能添加成员变量
说法二
因为category是运行时添加的,他只能操作class_rw_t结构,但是class_rw_t结构没有添加成员变量的入口。成员变量是存储在class_ro_t中的,是无法被修改的,以是category就不能添加成员变量。
在 Objective-C 中,分类(category)不能添加成员变量。这是因为分类在运行时动态添加,它只能操作 class_rw_t 结构,而 class_rw_t 结构没有提供添加成员变量的入口。成员变量的信息是存储在 class_ro_t 中的,并且在运行时是不可修改的。第一种说法不准确,不能仅仅因为源码中分类的定义没有成员变量列表就得出分类不能添加成员变量的结论,关键在于运行时的结构和机制限制
哦对了要留意有一个点
分类中的方法与原始类以及父类方法相比具有更高优先级,如果覆盖父类的方法,大概导致super消息的断裂。因此,最好不要覆盖原始类中的方法
我们知道一个类的实例方法存储在类内里,全部的类方法在元类内里,而对象调用方法isa指针先到相应的类的和元类,然后在其方法列表中查找
那么分类是这么实现这一功能的?
留个悬念 这周进度不够了 我得先写其他博客
isMemberOfClass & isKindOfClass(类和对象)
老例子 上总结
图解isKindOfClass和isMemberOfClass方法
总结
当调用者是——类对象SubClass
+(BOOL)isKindOfClass Class)cls :
判定cls是不是 元类->父类的元类->父父类的元类->…->根元类->NSObject (元类的superclass继承链)此中一个。
cls 传除NSObject.class外的任意类对象均为false。
+(BOOL)isMemeberOfClass Class)cls :
判定cls是不是本身的isa,
当调用者是——元类MetaClass
+(BOOL)isKindOfClass Class)cls :
判定cls是不是 根元类->NSObject 中的任意一个
+(BOOL)isMemeberOfClass Class)cls :
判定cls是不是根元类
当调用者是——对象Obj
-(BOOL)isKindOfClass Class)cls :
判定cls是不是类对象->父类->…->NSObject(superclass继承链)此中一个
-(BOOL)isMemeberOfClass Class)cls :
判定 cls 是不是本身的 类(类对象)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |