iOS runtime 部分一

2020-07-13  本文已影响0人  飞不越疯人院

主要讲解 isa 指针/方法的底层结构;

文中使用的 objc4源码是objc-781版本;

runtime 部分一
runtime 部分二
runtime 部分三


1. 首先记录一些基础的知识点

1.1 一个16进制位等于四个二进制的位;
例如

0000(十进制)=0000(二进制)=0(十六进制);
0001(十进制)=0001(二进制)=1(十六进制); 
0002(十进制)=0010(二进制)=2(十六进制); 
0003(十进制)=0011(二进制)=3(十六进制); 
0004(十进制)=0100(二进制)=4(十六进制); 
0005(十进制)=0101(二进制)=5(十六进制); 
0006(十进制)=0110(二进制)=6(十六进制); 
0007(十进制)=0111(二进制)=7(十六进制); 
0008(十进制)=1000(二进制)=8(十六进制); 
0009(十进制)=1001(二进制)=9(十六进制); 
0010(十进制)=1010(二进制)=A(十六进制); 
0011(十进制)=1011(二进制)=B(十六进制); 
0012(十进制)=1100(二进制)=C(十六进制); 
0013(十进制)=1101(二进制)=D(十六进制); 
0014(十进制)=1110(二进制)=E(十六进制); 
0015(十进制)=1111(二进制)=F(十六进制);

1.2 一些常见的二进制位操作
位运算 &

   0001 1000
&  0000 1001
--------------
   0000 1000

位运算 |

    0001 1000
|   0000 1001
--------------
    0001 1001

位运算 ~

     ~0001 1000
 --------------
      1110 0111

位的左右移动

 0x0000 0001  = 1;
 
 0x0000 0001<<0 =   0x0000 0001
 0x0000 0001<<1 =   0x0000 0010
 0x0000 0001<<2 =   0x0000 0100
  
 1<<0 = 0x0000 0001
 1<<1 = 0x0000 0010
 1<<2 = 0x0000 0100

取出一个二进制地址中的某一段如何操作?
例如取出0x1000 1000 1001 1000 中间的红色那8位;
0x1000 1000 1001 1000
& 0x0000 1111 1111 0000 = 0x0000 1000 0001 0000 = 0x1000 0001 0000

2. 通过共用体只用一个字节存储若干个 BOOL 值;

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
通过上述的基本知识和位域的简单知识, 可以设计如下代码;

///.h 的接口定义
NS_ASSUME_NONNULL_BEGIN
///通过共用体和位域的操作实现一个字节存放几个BOOL类型的变量
@interface Person : NSObject
- (void)setBig:(BOOL)big;
- (void)setMiddle:(BOOL)middle;
- (void)setSmall:(BOOL)small;

- (BOOL)big;
- (BOOL)middle;
- (BOOL)small;
@end
NS_ASSUME_NONNULL_END
///.m 的实现
#import "Person.h"
///占用一个字节的共用体
union {
    char bits;
    ///下面结构是为了方便阅读, 并没有实际的意义
    struct {
        ///位域操作, big 只占用一位
        char big : 1;
        ///位域操作, middle 只占用一位
        char middle : 1;
        ///位域操作, small 只占用一位
        char small : 1;
    };
}AllBool;

///通过位的左移定义掩码
#define  BigMask    (1<<0)
#define  MiddleMask (1<<1)
#define  SmallMask  (1<<2)

@implementation Person
- (void)setBig:(BOOL)big {
    if (big) {
        AllBool.bits |= BigMask;
    }else {
        AllBool.bits &= ~BigMask;
    }
}
- (void)setMiddle:(BOOL)middle {
    if (middle) {
        AllBool.bits |= MiddleMask;
    }else {
        AllBool.bits &= ~MiddleMask;
    }
}
- (void)setSmall:(BOOL)small {
    if (small) {
        AllBool.bits |= SmallMask;
    }else {
        AllBool.bits &= ~SmallMask;
    }
}

- (BOOL)big {
   return !!(AllBool.big & BigMask);
}
- (BOOL)middle {
    return !!(AllBool.middle & MiddleMask);
}
- (BOOL)small {
     return !!(AllBool.small & SmallMask);
}
@end

3. isa 指针的结构

通过 objc4-781源码可以得到isa的结构如下;

///底层是一个共用体
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ///isa 的位域信息
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};
===>
///arm64构架
# 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)
/// __x86_64__构架
# 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)

# else
#   error unknown architecture for packed isa
# endif

真机64位架构简化上述结构为:
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
  struct {
      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
  }
};

nonpointer : 0: 代表普通指针, 存储着Class, Mete-Class对象的内存地址; 1: 代表优化过, 使用位域存储更多信息;
has_assoc : 是否设置过关联对象(注意只要设置过即使移出了, 也算是关联过), 如果没有关联对象, 释放时会更快;
has_cxx_dtor : 是否有C++的析构函数; 如果没有则释放时更快;
shiftcls : 存放着Class或者Meta-Class的地址(通过将isa的地址&ISA_MASK即可得到);
magic: 用于分别在调试时对象是否已经完成初始化;
weakly_referenced: 标记是否弱引用指向过, 如果没有则释放时更快;
deallocating: 标记对象是否正在释放;
has_sidetable_rc : 用来标记引用计数是否过大不能存放在extra_rc, 如果是1, 引用计数存放在类的的一个属性SideTable中;
extra_rc : 存放引用计数(是减一后的值);

从64位构架开始 isa 的地址并不是直接是类对象或者元类对象而是需要& ISA_MASK才能得到其地址, 在arm64构架下 ISA_MASK = 0x0000000ffffffff8ULL ;而0x0000000ffffffff8的二进制为如下

ISA_MASK
isa&ISAac_MASK
另外我们可以推断出OC中的任何 类对象元类对象的地址, 最后三个位一定是000; 16进制展示是就是末位一定是0或者8;


参考文章和下载链接
测试代码
C 共用体
共用体详解
C 语言结构体位域
位域的操作
二进制的位操作
Apple 一些源码的下载地址

上一篇下一篇

猜你喜欢

热点阅读