健壮的实例变量 (Non Fragile ivars)和脆弱的实例变量(Fragile ivars)

一、Non Fragile ivars

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偏移字节
在编译类时,编译器生成了一个 ivar 布局,显示了在类中从哪能够访问ivars 对 ivar 的访问就能够经过 对象地址 + ivar偏移字节的方法

父类增长实例变量

苹果更新了NSObject类,发布新版本的系统,当增长了父类的ivar,这个时候布局就出错了,就不得不从新编译子类来恢复兼容性。 (那若是是在线上运行的app,升级系统后就没办法运行了)函数

兼容父类的变化

使用 Non Fragile ivars时,Runtime会进行检测来调整类中新增的 ivar 的偏移量。 这样就能够经过对象地址 + 基类大小 + ivar偏移字节 的方法来计算出ivar相应的地址,并访问到相应的ivar。(即便升级iOS系统,以前的app也能正常运行)布局

二、Non Fragile ivars的意义

Objective-C的库今后具备了**“二进制兼容性”**。 好比在项目里用了第三方提供的静态库SDK,包含一些.h和一个.a文件。当iOS SDK的版本从11升到了12,都不须要更新这个SDK。虽然iOS SDK版本升级时,苹果在等基类中加入了更多的成员变量,可是之前发布的静态库SDK不须要从新编译还能正常使用。spa

iOS从一开始就是用的modern runtime。 之前的Mac开发者每次MacOS发布新版本,都要从新编译本身的程序,跟着发布新版本。3d

三、引伸-为何OC类不能动态添加成员变量

既然容许用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中管理的,无论如何增删类方法,都不影响类实例的内存布局,已经建立出的类实例仍然可正常使用。

相关文章
相关标签/搜索