未命名文件 (8).jpg

内存布局

  • static修饰的在全局区,不占用内存
  • 内核分为用户态核和内核,4GB的空间,3GB供用户态使用,1GB供系统内核(系统消息处理、msgTrap等)使用
  • 栈区通过sp寄存器定位

未命名文件 (9).jpg

TaggedPointer

  • 专门存储小对象(指针+内容),NSNumber、NSdate、NSIndexPath等,
    • objc_msgSend速度加快,内存读取上有着倍的效率,创建时比以前快106倍
    • 内存优化,TaggedPointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free
    • OC中不能自定义TaggedPointer,Swift中则可以自定义TaggedPointer
    • 不要通过地址开头去判断类型,如0xa开头代表String,会有问题
  • payload = (decoded_obj << payload_lshift) >> payload_rshift,有效负债,通过左移、右移(加解密)得到,类似nonPointerISA中的shiftCls
    1. 传入地址ptr,通过异或混淆(objc_debug_taggedpointer_obfuscator随机数)解密
    2. 通过右移、左移 & mask得到真实的指针地址 ```objectivec static inline uintptr_t _objc_decodeTaggedPointer_noPermute(const void * _Nullable ptr) { uintptr_t value = (uintptr_t)ptr;

      if OBJC_SPLIT_TAGGED_POINTERS

      if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK) return value;

      endif

      return value ^ objc_debug_taggedpointer_obfuscator; }

static inline uintptr_t _objc_decodeTaggedPointer(const void * _Nullable ptr) { uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);

if OBJC_SPLIT_TAGGED_POINTERS

  1. uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
  2. value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
  3. value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;

endif

  1. return value;

}

  1. <a name="OxkKA"></a>
  2. ### 数据结构
  3. - **通过构造打印string地址,查看二进制得知(arm64环境)**
  4. - **首位1代表是小对象类型,0代表非小对象类型**
  5. - **末尾三位代表了数据类型,这里010为2,代表String类型**
  6. - **第二个倒数三位代表了数据的长度,以下分别的2,1,5**
  7. - **NSNumber则是代表内部数据类型的长度**
  8. - **char 0;short 1;int 2; long 3; float 4; double 5**
  9. - **余下倒数的数据代表了当前存储的ASCII值**
  10. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860440/1631086557640-41635913-683f-4fbc-88fe-93bda6b8a6a1.png#clientId=uac966c4a-6f32-4&from=paste&height=1066&id=ua2ab2d0e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1066&originWidth=2162&originalType=binary&ratio=1&size=396719&status=done&style=none&taskId=ubdb9ff7e-efcf-4574-9f0e-e67bc6b7070&width=2162)
  11. <a name="bjmhv"></a>
  12. ### 问题
  13. - 运行demo1不会崩溃,demo2会崩溃
  14. - demo1使用小对象存储,self.nameStr retain release操作的直接返回,没有ARC相关的计数管理,所以不会崩溃
  15. - **demo2使用堆存取,内存有开销,多线程异步读写时(对新值得Retain,旧值得Release),对同一片内存空间进行操作,某一刻容易发生多次release,对野指针进行了操作,导致崩溃**
  16. ```objectivec
  17. - (void)taggedPointerDemo {
  18. self.queue = dispatch_queue_create("com.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
  19. for (int i = 0; i<10000; i++) {
  20. dispatch_async(self.queue, ^{
  21. self.nameStr = [NSString stringWithFormat:@"cooci"];
  22. NSLog(@"%@",self.nameStr);
  23. });
  24. }
  25. }
  26. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  27. NSLog(@"来了");
  28. // 多线程 读和写
  29. // setter -> retian release
  30. for (int i = 0; i<1000000; i++) {
  31. dispatch_async(self.queue, ^{
  32. self.nameStr = [NSString stringWithFormat:@"cooci_你瞅啥,瞅你咋地哈哈哈哈哈哈哈"];
  33. NSLog(@"%@",self.nameStr);
  34. });
  35. }
  36. }
  • 内容长度超过一定值时,TaggedPointer无法满足存储,则变为普通存储

image.png

ARC & MRC

  • ARC是LLVM和Runtime配合的结果
  • ARC中禁止手动调用retain/release/retainCount/dealloc
  • ARC新加了weak、strong属性关键字

未命名文件 (11).jpg

Retain

  • 调用流程图

未命名文件 (12).jpg

  • 首先判断了是否为小对象类型,如果是,则直接return,验证了上述小对象类型未崩溃的原因
  • 获取对象isa,原因isa中存储了extrc引用计数,需要处理
  • do while循环中

    • 判断新isa是否为nonpointer

      • 不是的话,直接将Sidetable内容取出,refcntStorage进行+2(位置+1,1<<2 = 010)处理
      • 是的话,判断是否正在析构
      • 对newIsa.bits进行操作 + 1, carry
      • Carry溢出时,newisa.extra_rc = 1<<18, 新的地方散列表存一半,extra_rc存一半 ```objectivec ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant) { if (slowpath(isTaggedPointer())) return (id)this;

      bool sideTableLocked = false; bool transcribeToSideTable = false;

      isa_t oldisa; isa_t newisa;

      oldisa = LoadExclusive(&isa.bits);

      do { transcribeToSideTable = false; newisa = oldisa; if (slowpath(!newisa.nonpointer)) {

      1. ClearExclusive(&isa.bits);
      2. if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
      3. else return sidetable_retain(sideTableLocked);

      } // don’t check newisa.fast_rr; we already called any RR overrides if (slowpath(newisa.isDeallocating())) {

      1. ClearExclusive(&isa.bits);
      2. if (sideTableLocked) {
      3. ASSERT(variant == RRVariant::Full);
      4. sidetable_unlock();
      5. }
      6. if (slowpath(tryRetain)) {
      7. return nil;
      8. } else {
      9. return (id)this;
      10. }

      } uintptr_t carry; newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++

      if (slowpath(carry)) {

      1. // newisa.extra_rc++ overflowed
      2. if (variant != RRVariant::Full) {
      3. ClearExclusive(&isa.bits);
      4. return rootRetain_overflow(tryRetain);
      5. }
      6. // Leave half of the retain counts inline and
      7. // prepare to copy the other half to the side table.
      8. if (!tryRetain && !sideTableLocked) sidetable_lock();
      9. sideTableLocked = true;
      10. transcribeToSideTable = true;
      11. newisa.extra_rc = RC_HALF;
      12. newisa.has_sidetable_rc = true;

      } } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

      if (variant == RRVariant::Full) { if (slowpath(transcribeToSideTable)) {

      1. // Copy the other half of the retain counts to the side table.
      2. sidetable_addExtraRC_nolock(RC_HALF);

      }

      if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); } else { ASSERT(!transcribeToSideTable); ASSERT(!sideTableLocked); }

      return (id)this; } ```

      Release

  • 同Retain类似,当计算为0时,发送消息dealloc

    1. if (performDealloc) {
    2. ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    3. }

    Dealloc

    音视频设备模块框架图 (11).jpg