objc_class 结构体bash
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;
复制代码
objc_ivar结构体app
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
复制代码
对象地址 + ivar偏移字节
的方法
苹果更新了NSObject类,发布新版本的系统,当增长了父类的ivar,这个时候布局就出错了,就不得不从新编译子类来恢复兼容性。 (那若是是在线上运行的app,升级系统后就没办法运行了)函数
使用 Non Fragile ivars
时,Runtime会进行检测来调整类中新增的 ivar 的偏移量。 这样就能够经过对象地址 + 基类大小 + ivar偏移字节
的方法来计算出ivar相应的地址,并访问到相应的ivar。(即便升级iOS系统,以前的app也能正常运行)布局
Objective-C的库今后具备了**“二进制兼容性”**。 好比在项目里用了第三方提供的静态库SDK,包含一些.h和一个.a文件。当iOS SDK的版本从11升到了12,都不须要更新这个SDK。虽然iOS SDK版本升级时,苹果在等基类中加入了更多的成员变量,可是之前发布的静态库SDK不须要从新编译还能正常使用。spa
iOS从一开始就是用的modern runtime。 之前的Mac开发者每次MacOS发布新版本,都要从新编译本身的程序,跟着发布新版本。3d
既然容许用Category给类增长方法和属性,那为何不容许增长成员变量? 在Objective-C提供的runtime函数中,确实有一个class_addIvar()
函数用于给类添加成员变量,可是文档中特别说明:指针
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.code
这个函数只能在“构建一个类的过程当中”
调用。 一旦完成类定义,就不能再添加成员变量了。 通过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。 程序在运行时动态构建的类须要在调用objc_registerClassPair
以后才能够被使用,一样没有机会再添加成员变量。cdn
为基类动态增长成员变量会致使全部已建立出的子类实例都没法使用。对象
那为何runtime容许动态添加方法和属性,而不会引起问题呢?
由于方法和属性并不“属于”类实例,而成员变量“属于”类实例。咱们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和全部的成员变量。 因此假如容许动态修改类成员变量布局,已经建立出的类实例就不符合类定义了,变成了无效对象。 但方法定义是在objc_class中管理的,无论如何增删类方法,都不影响类实例的内存布局,已经建立出的类实例仍然可正常使用。