1、位运算

在添加KVO时,配置参数可以通过按位或传入:

  1. [self addObserver:self forKeyPath:@"Title" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

在内部拿到的是按位或运算的结果,如果在这个结果中找出传入了那些数据?
自定义一个枚举,和setOpitions方法:

  1. typedef enum {
  2. OptionsOne = 1<<0,
  3. OptionsTwo = 1<<1,
  4. OptionsThree = 1<<2
  5. } Options;
  6. - (void)setOpitions:(Options)options;

*1<<2 代表1向前移动2位,即0b 0000 0100

观察OptionsOne、OptionsTwo、OptionsThree按位或运算的结果:

  1. 0b 0000 0001 // OptionsOne,最后一位是1
  2. 0b 0000 0010 // OptionsTwo,倒数第二位是1
  3. | 0b 0000 0100 // OptionsThree,倒数第三位是1
  4. ----------------
  5. 0b 0000 0111 // options,最后一位是1

最后三个option的数据都保存在了options里面。
如果想从结果中判断是否包含某个option,可以使用按位与运算来判断:

  1. 0b 0000 0111 // options,最后三位是1
  2. & 0b 0000 0001 // OptionsOne,最后一位是1
  3. ----------------
  4. 0b 0000 0001 // 结果,最后一位是1

可以发现,OptionsOne通过和options进行与运算结果还是OptionsOne,这样就能判断options是否包含某个元素,setOptions方法内部可以这样判断:

  1. - (void)setOpitions:(Options)options {
  2. if (OptionsOne & options) {
  3. // 包含OptionsOne
  4. }
  5. if (OptionsTwo & options) {
  6. // 包含OptionsTwo
  7. }
  8. if (OptionsThree & options) {
  9. // 包含OptionsThree
  10. }
  11. }

2、isa位域详解

  1. // isa共用体(arm64&真机,简化后)
  2. union isa_t {
  3. uintptr_t bits;
  4. Class cls;
  5. struct {
  6. uintptr_t nonpointer : 1; \
  7. uintptr_t has_assoc : 1; \
  8. uintptr_t has_cxx_dtor : 1; \
  9. uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
  10. uintptr_t magic : 6; \
  11. uintptr_t weakly_referenced : 1; \
  12. uintptr_t unused : 1; \
  13. uintptr_t has_sidetable_rc : 1; \
  14. uintptr_t extra_rc : 19
  15. };
  16. };

nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息

has_assoc
是否有设置过关联对象,如果没有,释放时会更快

has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快

shiftcls
存储着Class、Meta-Class对象的内存地址信息

magic
用于在调试时分辨对象是否未完成初始化

weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快

deallocating
对象是否正在释放

extra_rc
里面存储的值是引用计数器减1

has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中