美文网首页
iOS 底层 - runtime之isa详解-->取值、设值、位

iOS 底层 - runtime之isa详解-->取值、设值、位

作者: 水中的蓝天 | 来源:发表于2020-03-26 18:42 被阅读0次

    本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

    要想学习Runtime,首先要了解它底层的一些数据结构,比如isa指针

    在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址;
    从arm64架构开始,对isa进行了优化,变成了一个联合体(union)结构,还使用位域来存储更多的信息;至此isa需要进行一次位运算(&ISA_MASK)才能计算出真实地址;

    QQ20200326-104436@2x.png

    源码 objc4-779.1 --> objc-private.h

    位域宏定义(真机环境arm64)
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    #   define ISA_BITFIELD                                                      \
         uintptr_t nonpointer        : 1; 拿二进制的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)
    
    isa详解-位域@2x.png
    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // ISA_BITFIELD  在 isa.h文件中
        };
    #endif
    };
    
    struct objc_object {
    private:
        isa_t isa;     //arm64是 Class 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;
    
        // initIsa() should be used to init the isa of new objects only.
        // If this object already has an isa, use changeIsa() for correctness.
        // initInstanceIsa(): objects with no custom RR/AWZ
        // initClassIsa(): class objects
        // initProtocolIsa(): protocol objects
        // initIsa(): other objects
        void initIsa(Class cls /*nonpointer=false*/);
        void initClassIsa(Class cls /*nonpointer=maybe*/);
        void initProtocolIsa(Class cls /*nonpointer=maybe*/);
        void initInstanceIsa(Class cls, bool hasCxxDtor);
    
        // changeIsa() should be used to change the isa of existing objects.
        // If this is a new object, use initIsa() for performance.
        Class changeIsa(Class newCls);
    
        bool hasNonpointerIsa();
        bool isTaggedPointer();
        bool isBasicTaggedPointer();
        bool isExtTaggedPointer();
        bool isClass();
    
        // object may have associated objects?
        bool hasAssociatedObjects();
        void setHasAssociatedObjects();
    
        // object may be weakly referenced?
        bool isWeaklyReferenced();
        void setWeaklyReferenced_nolock();
    
        // object may have -.cxx_destruct implementation?
        bool hasCxxDtor();
    
        // Optimized calls to retain/release methods
        id retain();
        void release();
        id autorelease();
    
        // Implementations of retain/release methods
        id rootRetain();
        bool rootRelease();
        id rootAutorelease();
        bool rootTryRetain();
        bool rootReleaseShouldDealloc();
        uintptr_t rootRetainCount();
    
        // Implementation of dealloc methods
        bool rootIsDeallocating();
        void clearDeallocating();
        void rootDealloc();
    
    private:
        void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
    
        // Slow paths for inline control
        id rootAutorelease2();
        uintptr_t overrelease_error();
    
    #if SUPPORT_NONPOINTER_ISA
        // Unified retain count manipulation for nonpointer isa
        id rootRetain(bool tryRetain, bool handleOverflow);
        bool rootRelease(bool performDealloc, bool handleUnderflow);
        id rootRetain_overflow(bool tryRetain);
        uintptr_t rootRelease_underflow(bool performDealloc);
    
        void clearDeallocating_slow();
    
        // Side table retain count overflow for nonpointer isa
        void sidetable_lock();
        void sidetable_unlock();
    
        void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
        bool sidetable_addExtraRC_nolock(size_t delta_rc);
        size_t sidetable_subExtraRC_nolock(size_t delta_rc);
        size_t sidetable_getExtraRC_nolock();
    #endif
    
        // Side-table-only retain count
        bool sidetable_isDeallocating();
        void sidetable_clearDeallocating();
    
        bool sidetable_isWeaklyReferenced();
        void sidetable_setWeaklyReferenced_nolock();
    
        id sidetable_retain();
        id sidetable_retain_slow(SideTable& table);
    
        uintptr_t sidetable_release(bool performDealloc = true);
        uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);
    
        bool sidetable_tryRetain();
    
        uintptr_t sidetable_retainCount();
    #if DEBUG
        bool sidetable_present();
    #endif
    };
    
    

    位运算之 &

    &的作用:可以用来取出特定的位

       0011
      &0010
    --------
       0010
    
    取出倒数第二位的值 1 ;方法:找到对应掩码按位与(&) ;
    
    

    位运算之 |

        0010 1000
     |  0000 0010 //掩码
    -------- 
        0010  1010
    
    取出倒数第二位的值 1 ;方法:找到对应掩码按位或(|) ;
    
    

    位运算之 ~ 按位取反

    ~0000 0010
    

    位运算之掩码

    掩码:一般用来按位与(&)运算的,通常用某某某Mask来命名

    //#define XYHTallMask 1
    //#define XYHRichMask 2
    //#define XYHHandsomeMask 4

    //#define XYHTallMask 0b00000001
    //#define XYHRichMask 0b00000010
    //#define XYHHandsomeMask 0b00000100

    // << 表示位移

    define XYHTallMask (1<<0)

    define XYHRichMask (1<<1)

    define XYHHandsomeMask (1<<2)

    三组宏一样,不过最后一组表达更简洁

    代码示例:

    @interface XYHPerson : NSObject
    
    - (void)setTall:(BOOL)tall;
    - (void)setRich:(BOOL)rich;
    - (void)setHandsome:(BOOL)handsome;
    
    - (BOOL)isTall;
    - (BOOL)isRich;
    - (BOOL)isHandsome;
    
    @end
    

    第一种写法

    #define XYHTallMask (1<<0)
    #define XYHRichMask (1<<1)
    #define XYHHandsomeMask (1<<2)
    
    @interface XYHPerson()
    {
        char _tallRichHansome;
    }
    @end
    
    @implementation XYHPerson
    
    // 0010 1010
    //&1111 1101
    //----------
    // 0010 1000
    - (instancetype)init
    {
        if (self = [super init]) {
            _tallRichHansome = 0b00000100;
        }
        return self;
    }
    
    - (void)setTall:(BOOL)tall
    {
        if (tall) {
            _tallRichHansome |= XYHTallMask;
        } else {
            _tallRichHansome &= ~XYHTallMask;
        }
    }
    
    - (BOOL)isTall
    {
        return !!(_tallRichHansome & XYHTallMask);
    }
    
    - (void)setRich:(BOOL)rich
    {
        if (rich) {
            _tallRichHansome |= XYHRichMask;
        } else {
            _tallRichHansome &= ~XYHRichMask;
        }
    }
    
    - (BOOL)isRich
    {
        return !!(_tallRichHansome & XYHRichMask);
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        if (handsome) {
            _tallRichHansome |= XYHHandsomeMask;
        } else {
            _tallRichHansome &= ~XYHHandsomeMask;
        }
    }
    
    - (BOOL)isHandsome
    {
    
        //   ‘!!’  双取反
        return !!(_tallRichHansome & XYHHandsomeMask);
    }
    
    @end
    
    

    第二种写法

    @interface XYHPerson()
    {
        // 位域
        struct {
            char tall : 1;
            char rich : 1;
            char handsome : 1;
        } _tallRichHandsome;
    }
    @end
    
    @implementation XYHPerson
    
    - (void)setTall:(BOOL)tall
    {
        _tallRichHandsome.tall = tall;
    }
    
    - (BOOL)isTall
    {
        return !!_tallRichHandsome.tall;
    }
    
    - (void)setRich:(BOOL)rich
    {
        _tallRichHandsome.rich = rich;
    }
    
    - (BOOL)isRich
    {
        return !!_tallRichHandsome.rich;
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        _tallRichHandsome.handsome = handsome;
    }
    
    - (BOOL)isHandsome
    {
        return !!_tallRichHandsome.handsome;
    }
    
    @end
    
    

    第三种写法 (苹果官方使用,如isa)

    
    #define XYHTallMask (1<<0)
    #define XYHRichMask (1<<1)
    #define XYHHandsomeMask (1<<2)
    #define XYHThinMask (1<<3)
    
    @interface XYHPerson()
    {
        union {
            char bits;
            struct {
                char tall : 1;
                char rich : 1;
                char handsome : 1;
                char thin : 1;
            };
      /**
       //作用是为了增加代码可读性,没有本质作用;值是存储在bits属性中的
       struct {
                char tall : 1;
                char rich : 1;
                char handsome : 1;
                char thin : 1;
            };
       */
        } _tallRichHandsome;
    }
    @end
    
    @implementation XYHPerson
    
    - (void)setTall:(BOOL)tall
    {
        if (tall) {
            _tallRichHandsome.bits |= XYHTallMask;
        } else {
            _tallRichHandsome.bits &= ~XYHTallMask;
        }
    }
    
    - (BOOL)isTall
    {
        return !!(_tallRichHandsome.bits & XYHTallMask);
    }
    
    - (void)setRich:(BOOL)rich
    {
        if (rich) {
            _tallRichHandsome.bits |= XYHRichMask;
        } else {
            _tallRichHandsome.bits &= ~XYHRichMask;
        }
    }
    
    - (BOOL)isRich
    {
        return !!(_tallRichHandsome.bits & XYHRichMask);
    }
    
    - (void)setHandsome:(BOOL)handsome
    {
        if (handsome) {
            _tallRichHandsome.bits |= XYHHandsomeMask;
        } else {
            _tallRichHandsome.bits &= ~XYHHandsomeMask;
        }
    }
    
    - (BOOL)isHandsome
    {
        return !!(_tallRichHandsome.bits & XYHHandsomeMask);
    }
    
    - (void)setThin:(BOOL)thin
    {
        if (thin) {
            _tallRichHandsome.bits |= XYHThinMask;
        } else {
            _tallRichHandsome.bits &= ~XYHThinMask;
        }
    }
    
    - (BOOL)isThin
    {
        return !!(_tallRichHandsome.bits & XYHThinMask);
    }
    
    @end
    
    

    小常识: 0xff 的有符号数就是-1,无符号数就是255

    union : 共用体,内部所有成员共用同一块内存;存储时要求不同的成员用不同的位存储,否则可能会覆盖其他成员的值,如0x0000 0000 从最后一位开始占多少位是需要定好的。

    为什么要&ISA_MASK才能获取到地址值 ?

    uintptr_t shiftcls 占33位,这里就是用来存放地址值的;ISA_MASK就是shiftcls的掩码,与(&)上这个掩码才能够拿到真正的地址。

    注意:类对象、元类对象的内存地址值(二进制)最后3位一定是0;

    证明:

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSLog(@"%p", [ViewController class]);
        NSLog(@"%p", object_getClass([ViewController class]));
    }
    打印结果:
    [16416:7767940] 0x10a2acd88
    [16416:7767940] 0x10a2acdb0
    

    一个16进制位代表4个二进制位

    看到输出的16进制地址末位要么是8 要么是 0,8的二进制是0b1000,0的二进制就是0b0000

    位运算的常用方法:

    //typedef enum {
    //    XYHOptionsOne = 1,   // 0b0001
    //    XYHOptionsTwo = 2,   // 0b0010
    //    XYHOptionsThree = 4, // 0b0100
    //    XYHOptionsFour = 8   // 0b1000
    //} XYHOptions;
    
    typedef enum {
    //    XYHOptionsNone = 0,    // 0b0000
        XYHOptionsOne = 1<<0,   // 0b0001
        XYHOptionsTwo = 1<<1,   // 0b0010
        XYHOptionsThree = 1<<2, // 0b0100
        XYHOptionsFour = 1<<3   // 0b1000
    } XYHOptions;
    
    @interface ViewController ()
    
    @end
    @implementation ViewController
    
    /*
     0b0001
     0b0010
     0b1000
     ------
       0b1011
    &0b0100
    -------
     0b0000
     */
    - (void)setOptions:(XYHOptions)options
    {
        if (options & XYHOptionsOne) {
            NSLog(@"包含了XYHOptionsOne");
        }
        
        if (options & XYHOptionsTwo) {
            NSLog(@"包含了XYHOptionsTwo");
        }
        
        if (options & XYHOptionsThree) {
            NSLog(@"包含了XYHOptionsThree");
        }
        
        if (options & XYHOptionsFour) {
            NSLog(@"包含了XYHOptionsFour");
        }
    }
    
    使用
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self setOptions: XYHOptionsOne | XYHOptionsFour];
    }
    
    

    总结:

    isa作用: 用来查找类对象、元类对象(实例对象的isa指向类对象,类对象的isa指向元类对象);
    在arm64之前ISA就是普通的指针,里面存储着类对象或者元类对象的地址,arm64之后isa采用共用体的数据结构定义,在64位里面存储了很多东西包括

    nonpointer 是否优化过
    has_assoc 是否设置过关联对象
    weakly_referenced 是否被弱引用指向过
    has_cxx_dtor 是否有析构函数, 是否调用过C++的函数 销毁成员变量
    shiftcls 类对象内存地址或者元类对象内存地址信息等
    其中shiftcls占用了33位,用来专门存储地址值
    extra_rc rc是Reference Counting的简写,代表引用计数; 里面存储的值是对象的引用计数减一
    has_sidetable_rc 引用计数是否过大无法存储在ISA中,如果是,那么引用计数会存储在一个叫SideTable的类的refcnts属性中

    疑问 :extra_rc中存储的值为什么是引用计数减一,直接存引用计数不香吗 ?

    不香

    • 一个对象被初始化出来引用计数默认是1,引用计数的值是存储在对象本身的ISA中的;减去这个1是不是直接就得到了持有该对象的数量 ?直接把减一后的值存进去更合理
    • 减一后数值就会相对变小,而且引用计数是在不断变化的,有限内存下存储越小的值,就意味着越节省空间
    • 当extra_rc为0时就表示,这个对象将要被释放了。0这个值可能会在其他地方被当做标记来用(个人猜测);

    注意这里是否优化过、设置过表达的意思是:只要有过就算,而不是现在有没有;只要有过就会有值。

    相关文章

      网友评论

          本文标题:iOS 底层 - runtime之isa详解-->取值、设值、位

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