美文网首页
OC底层原理(三)、isa、对象和类结构、属性粗略分析

OC底层原理(三)、isa、对象和类结构、属性粗略分析

作者: 默默_David | 来源:发表于2020-09-18 17:40 被阅读0次

    OC底层原理汇总

    从探索isa的初始化开始

    OC底层原理(一).alloc实际调用流程分析内容最后的流程图中,_class_createInstanceFromZone,我们分为三步:

    • 1、size = cls->instanceSize(extraBytes);获取对象需要分配的内存大小
    • 2、obj = (id)calloc(1, size);如何申请内存
    • 3、obj->initInstanceIsa(cls, hasCxxDtor);初始化isa

    上一篇中我们分析了获取需要分配的大小,以及具体如何分配内存,也就是第1步和第2步。本篇我们继续对第三步进行探索,也就是isa的创建以及初始化。

    分析initInstanceIsa方法的实现

    inline void 
    objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
    {
        ASSERT(!cls->instancesRequireRawIsa());
        ASSERT(hasCxxDtor == cls->hasCxxDtor());
    
        initIsa(cls, true, hasCxxDtor);
    }
    

    initInstanceIsa内部调用了initIsa,并且传入第一个参数为cls(类的地址),第二个参数为true,第三个为hasCxxDtor,我们看看initIsa实现

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        ASSERT(!isTaggedPointer()); 
        
        if (!nonpointer) {
            //如果是纯指针的isa,那么isa仅仅用于存放类地址
            isa = isa_t((uintptr_t)cls);
        } else {
            ASSERT(!DisableNonpointerIsa);
            ASSERT(!cls->instancesRequireRawIsa());
    
            //分配一个isa_t变量newisa,并把所有位清零
            isa_t newisa(0);
            /*
             是否支持索引,默认苹果平台都不支持的
             SUPPORT_INDEXED_ISA为1表示类表的索引
            */
    #if SUPPORT_INDEXED_ISA
            ASSERT(cls->classArrayIndex() > 0);
            newisa.bits = ISA_INDEX_MAGIC_VALUE;
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.indexcls = (uintptr_t)cls->classArrayIndex();
    #else
            /*
             0x001d 8000 0000 0001ULL
             给isa一个初始值0x001d800000000001ULL
             在这个初始值中,已经设置了nonpointer和magic的值
             */
            newisa.bits = ISA_MAGIC_VALUE;
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
            /*
             将类的地址信息右移三位,放入预设好的类的存储位置
             */
            newisa.shiftcls = (uintptr_t)cls >> 3;
    #endif
            // This write must be performed in a single store in some cases
            // (for example when realizing a class because other threads
            // may simultaneously try to use the class).
            // fixme use atomics here to guarantee single-store and to
            // guarantee memory order w.r.t. the class index table
            // ...but not too atomic because we don't want to hurt instantiation
            isa = newisa;
        }
    }
    

    initIsa中,做了isa位域中四个初始化,分别是给magicnonpointerhas_cxx_dtorshiftcls赋了值。
    大致流程如下:

    initIsa流程

    OC底层原理(一).alloc实际调用流程分析内容中,我们知道initIsa来自于最初的alloc调用。也就是说,一个类调用alloc,除了分配内存以及内存对齐外,它还对内部的成员变量isa作了初始化,每个对象一定有一个叫isa的成员变量,那么,isa到底是什么呢?

    isa是什么

    使用clang重写分析

    我们在main.m中创建一个继承自NSObject的类LWPerson,代码如下:

    #import <Foundation/Foundation.h>
    
    @interface LWPerson : NSObject
    
    @property (nonatomic,copy) NSString *name;
    
    @property (nonatomic,assign) short age;
    
    @property (nonatomic,assign) BOOL isMan;
    
    
    @end
    
    @implementation LWPerson
    
    + (void)LWPersonClassMethod{
        NSLog(@"%s",__func__);
    }
    - (int)LWPersonInstanceMethod{
        NSLog(@"%s",__func__);
        NSArray *array = @[@1,@2,@3];
        return [array[1] intValue];
    }
    
    @end
    int main(int argc, const char * argv[]) {
        
        LWPerson *person = [LWPerson alloc];
        person.name = @"Jobs";
        person.age = 5;
        person.isMan = YES;
        
        [person LWPersonInstanceMethod];
        [LWPerson LWPersonClassMethod];
        
        return 0;
    }
    

    我们在终端使用如下clang命令将main.m重写为C、C++实现

    clang -rewrite-objc main.m -o main.cpp
    

    执行命令后,在目录中生成一个main.cpp文件,我们打开它,搜索LWPerson,我们删除和main中无关的数据后,得到最后的代码如下:

    #ifndef _REWRITER_typedef_LWPerson
    #define _REWRITER_typedef_LWPerson
    typedef struct objc_object LWPerson;
    typedef struct {} _objc_exc_LWPerson;
    #endif
    
    extern "C" unsigned long OBJC_IVAR_$_LWPerson$_name;
    extern "C" unsigned long OBJC_IVAR_$_LWPerson$_age;
    extern "C" unsigned long OBJC_IVAR_$_LWPerson$_isMan;
    struct LWPerson_IMPL {
        //这就是isa
        struct NSObject_IMPL NSObject_IVARS;
        BOOL _isMan;
        short _age;
        NSString *_name;
    };
    
    
    // @property (nonatomic,copy) NSString *name;
    
    // @property (nonatomic,assign) short age;
    
    // @property (nonatomic,assign) BOOL isMan;
    
    
    /* @end */
    
    
    // @implementation LWPerson
    
    //类方法重写
    static void _C_LWPerson_LWPersonClassMethod(Class self, SEL _cmd) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_h4_g62cv3xn0ys3gpzsj_wjmtmh0000gn_T_main_8bac4e_mi_0,__func__);
    }
    //实例方法重写
    static int _I_LWPerson_LWPersonInstanceMethod(LWPerson * self, SEL _cmd) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_h4_g62cv3xn0ys3gpzsj_wjmtmh0000gn_T_main_8bac4e_mi_1,__func__);
        NSArray *array = ((NSArray *(*)(Class, SEL, ObjectType  _Nonnull const * _Nonnull, NSUInteger))(void *)objc_msgSend)(objc_getClass("NSArray"), sel_registerName("arrayWithObjects:count:"), (const id *)__NSContainer_literal(3U, ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 1), ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 2), ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3)).arr, 3U);
        return ((int (*)(id, SEL))(void *)objc_msgSend)((id)((id (*)(id, SEL, NSUInteger))(void *)objc_msgSend)((id)array, sel_registerName("objectAtIndexedSubscript:"), (NSUInteger)1), sel_registerName("intValue"));
    }
    
    //name的getter方法
    static NSString * _I_LWPerson_name(LWPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LWPerson$_name)); }
    extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
    
    //name的setter方法
    static void _I_LWPerson_setName_(LWPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LWPerson, _name), (id)name, 0, 1); }
    //age的getter方法
    static short _I_LWPerson_age(LWPerson * self, SEL _cmd) { return (*(short *)((char *)self + OBJC_IVAR_$_LWPerson$_age)); }
    //age的setter方法
    static void _I_LWPerson_setAge_(LWPerson * self, SEL _cmd, short age) { (*(short *)((char *)self + OBJC_IVAR_$_LWPerson$_age)) = age; }
    //isMan的setter方法
    static BOOL _I_LWPerson_isMan(LWPerson * self, SEL _cmd) { return (*(BOOL *)((char *)self + OBJC_IVAR_$_LWPerson$_isMan)); }
    //isMan的setter方法
    static void _I_LWPerson_setIsMan_(LWPerson * self, SEL _cmd, BOOL isMan) { (*(BOOL *)((char *)self + OBJC_IVAR_$_LWPerson$_isMan)) = isMan; }
    // @end
    int main(int argc, const char * argv[]) {
        //alloc方法调用
        LWPerson *person = ((LWPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LWPerson"), sel_registerName("alloc"));
        //调用setName:
        ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_h4_g62cv3xn0ys3gpzsj_wjmtmh0000gn_T_main_8bac4e_mi_2);
        //调用setAge:
        ((void (*)(id, SEL, short))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), (short)5);
        //调用setIsMan:
        ((void (*)(id, SEL, BOOL))(void *)objc_msgSend)((id)person, sel_registerName("setIsMan:"), ((bool)1));
    
        //调用实例方法LWPersonInstanceMethod
        ((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("LWPersonInstanceMethod"));
        //调用类方法LWPersonClassMethod
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LWPerson"), sel_registerName("LWPersonClassMethod"));
    
        return 0;
    }
    

    从上面的代码可以看出以下几点:

    • 1.OC类和对象底层都是结构体
    • 2.每个对象都有一个默认的isa变量,它由NSObject继承而来
    • 3.属性的本质,就是实例变量+setter+getter
    • 4.实例变量值的获取,就是对象首地址+地址偏移
    • 5.属性值的设置,对于简单类型值,直接通过地址偏移设置,其他底层都是调用了objc_setProperty函数。
    • 6.方法的调用,都是使用objc_msgSend发送消息

    OC类和对象底层都是结构体

    我们打开objc781源码,可以在objc-private.h中找到struct objc_object的定义,如下所示:

    struct objc_object {
    private:
        isa_t isa;
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
        // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
        Class rawISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
        
        uintptr_t isaBits() const;
        ......
    }
    

    我们看到struct objc_object中,有一个成员变量isa,另外有三个获取isa的成员方法:

    • Class ISA()方法用于非taggedPointer对象获取isa
    • Class rawISA();方法用于非taggedPointer对象非nonpointer对象获取isa
    • Class getIsa();方法用于taggedPointer对象获取isa

    另外,它还提供了直接获取isabits成员的方法uintptr_t isaBits() const,用const在函数末尾修饰,代表一个常成员函数,仅能读取。

    此外,我们可以看到我们常用于指向对象类型的id类型,它是struct objc_object *的别名。

    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    

    objc-runtime-new.h文件中,我们可以找到struct 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
    
        class_rw_t *data() const {
            return bits.data();
        }
    }
    

    从上面可以看出,struct objc_class继承于objc_object,它有四个成员:

    • isa,来自于它的父类,isa_t类型
    • superclass,它是一个指向父类的指针
    • cachecache_t类型,存储缓存的结构体
    • bitsclass_data_bits_t类型的结构体,存储了类中方法、协议、成员列表等的信息。

    同样,我们可以找到关于Class的定义,它是struct objc_class *的别名

    /// An opaque type that represents an Objective-C class.
    typedef struct objc_class *Class;
    

    对象和类结构如下图


    对象与类结构体

    isa结构

    我们可以找到isa的定义,如下图所示:

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    

    从代码我们可以看出,isa_t是一个联合体,它有两个成员,分别为Class clsuintptr_t bits

    我们知道,所谓联合体,也叫共用体,它的所有成员变量共用一段内存,它的大小等于最大的成员变量的大小,所以,我们可以得出,isa_t内存所占大小为8字节,64位

    我们再看定义中更重要的一段定义ISA_BITFIELD;,它在isa.h中,如下所示

    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    #   define ISA_BITFIELD                                                      \
          uintptr_t nonpointer        : 1;                                       \
          uintptr_t has_assoc         : 1;                                       \
          uintptr_t has_cxx_dtor      : 1;                                       \
          uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
          uintptr_t magic             : 6;                                       \
          uintptr_t weakly_referenced : 1;                                       \
          uintptr_t deallocating      : 1;                                       \
          uintptr_t has_sidetable_rc  : 1;                                       \
          uintptr_t extra_rc          : 19
    #   define RC_ONE   (1ULL<<45)
    #   define RC_HALF  (1ULL<<18)
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    #   define ISA_MAGIC_MASK  0x001f800000000001ULL
    #   define ISA_MAGIC_VALUE 0x001d800000000001ULL
    #   define ISA_BITFIELD                                                        \
          uintptr_t nonpointer        : 1;                                         \
          uintptr_t has_assoc         : 1;                                         \
          uintptr_t has_cxx_dtor      : 1;                                         \
          uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
          uintptr_t magic             : 6;                                         \
          uintptr_t weakly_referenced : 1;                                         \
          uintptr_t deallocating      : 1;                                         \
          uintptr_t has_sidetable_rc  : 1;                                         \
          uintptr_t extra_rc          : 8
    #   define RC_ONE   (1ULL<<56)
    #   define RC_HALF  (1ULL<<7)
    

    我们先不讨论内部的执行结果,我们先谈谈关于联合体中定义struct,而struct中定义一连串的成员,这个是什么呢,在C语言中,我们将它叫做位域

    位域

    位域,也叫位段,它是一种特殊的结构体类型,其所有成员的长度均是以二进制位为单位定义的,结构体的成员被称为位段,位段定义的一般形式如下:

    struct 结构名
    {
      类型 变量名1:长度
      类型 变量名2:长度
      ……
      类型 变量名n:长度
    }
    

    位段定义类型必须是intunsignedsigned中的一种,也就是必须为整形

    位段的特性如下:

    • 从低位到高位排列
    • 一个位段必须存在一个存储单元中,不能跨两个存储单元,如果本单元不够容纳某位段,则从下一个单元开始存储该位段
    • 可以用%d%x%u%o等格式字符,以整数形式输出位段
    • 在数值表达式中引用位段时,系统自动将位段转换为整形数。

    isa中的位域意义

    isa位段定义的意义如下图所示

    isa位域

    在不同平台下,isa的内部存储区域如下图

    isa位域占位

    使用lldb对验证isa存储类地址

    在之前,我们提到struct objc_obejct的成员函数ISA()是获取nonpointer isa的类地址的方法,我们来看看它的实现

    inline Class 
    objc_object::ISA() 
    {
        ASSERT(!isTaggedPointer()); 
    #if SUPPORT_INDEXED_ISA
        if (isa.nonpointer) {
            uintptr_t slot = isa.indexcls;
            return classForIndex((unsigned)slot);
        }
        return (Class)isa.bits;
    #else
        return (Class)(isa.bits & ISA_MASK);
    #endif
    }
    

    核心代码为(Class)(isa.bits & ISA_MASK);,这一步将isa的数据与一个ISA_MASK进行按位与,就得到了类的地址信息。

    define ISA_MASK        0x0000000ffffffff8ULL
    

    我们使用一开始例子,打断点如下


    断点

    然后断点执行到这一步时候,我们使用如下命令得到结果

    (lldb) x/4gx person
    0x10054aa30: 0x001d8001000032f5 0x0000000000050001
    0x10054aa40: 0x0000000100002040 0x0000000000000000
    (lldb) po 0x001d8001000032f5 & 0x0000000ffffffff8ULL
    LWPerson
    

    这样就验证了isa是存储类信息的。

    objc_setProperty源码分析

    objc_setProperty源码如下:

    void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
    {
        bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
        bool mutableCopy = (shouldCopy == MUTABLE_COPY);
        reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
    }
    
    static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
    {
        if (offset == 0) {
            //如果偏移量为0,就是设置isa,也就是设置类地址信息
            object_setClass(self, newValue);
            return;
        }
    
        id oldValue;
        id *slot = (id*) ((char*)self + offset);
    
        if (copy) {
            newValue = [newValue copyWithZone:nil];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:nil];
        } else {
            if (*slot == newValue) return;
            //新值retain
            newValue = objc_retain(newValue);
        }
    
        if (!atomic) {
            oldValue = *slot;
            //进行赋值
            *slot = newValue;
        } else {
            spinlock_t& slotlock = PropertyLocks[slot];
            slotlock.lock();
            oldValue = *slot;
            //进行赋值
            *slot = newValue;        
            slotlock.unlock();
        }
        //旧值release
        objc_release(oldValue);
    }
    

    从源码中我们可以看到,retainrelease的操作在底层已经帮我们完成了,所有涉及引用计数的属性都要调用到objc_setProperty方法,这是一种适配器模式的思想,它大大的节约了我们的代码量。

    相关文章

      网友评论

          本文标题:OC底层原理(三)、isa、对象和类结构、属性粗略分析

          本文链接:https://www.haomeiwen.com/subject/caijyktx.html