内存布局
- static修饰的在全局区,不占用内存
- 内核分为用户态核和内核,4GB的空间,3GB供用户态使用,1GB供系统内核(系统消息处理、msgTrap等)使用
- 栈区通过sp寄存器定位
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
- 传入地址ptr,通过异或混淆(objc_debug_taggedpointer_obfuscator随机数)解密
- 通过右移、左移 & 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
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
endif
return value;
}
<a name="OxkKA"></a>### 数据结构- **通过构造打印string地址,查看二进制得知(arm64环境)**- **首位1代表是小对象类型,0代表非小对象类型**- **末尾三位代表了数据类型,这里010为2,代表String类型**- **第二个倒数三位代表了数据的长度,以下分别的2,1,5**- **NSNumber则是代表内部数据类型的长度**- **char 0;short 1;int 2; long 3; float 4; double 5**- **余下倒数的数据代表了当前存储的ASCII值**<a name="bjmhv"></a>### 问题- 运行demo1不会崩溃,demo2会崩溃- demo1使用小对象存储,self.nameStr retain release操作的直接返回,没有ARC相关的计数管理,所以不会崩溃- **demo2使用堆存取,内存有开销,多线程异步读写时(对新值得Retain,旧值得Release),对同一片内存空间进行操作,某一刻容易发生多次release,对野指针进行了操作,导致崩溃**```objectivec- (void)taggedPointerDemo {self.queue = dispatch_queue_create("com.cooci.cn", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i<10000; i++) {dispatch_async(self.queue, ^{self.nameStr = [NSString stringWithFormat:@"cooci"];NSLog(@"%@",self.nameStr);});}}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{NSLog(@"来了");// 多线程 读和写// setter -> retian releasefor (int i = 0; i<1000000; i++) {dispatch_async(self.queue, ^{self.nameStr = [NSString stringWithFormat:@"cooci_你瞅啥,瞅你咋地哈哈哈哈哈哈哈"];NSLog(@"%@",self.nameStr);});}}
- 内容长度超过一定值时,TaggedPointer无法满足存储,则变为普通存储
ARC & MRC
- ARC是LLVM和Runtime配合的结果
- ARC中禁止手动调用retain/release/retainCount/dealloc
- ARC新加了weak、strong属性关键字
Retain
- 调用流程图

- 首先判断了是否为小对象类型,如果是,则直接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)) {
ClearExclusive(&isa.bits);if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;else return sidetable_retain(sideTableLocked);
} // don’t check newisa.fast_rr; we already called any RR overrides if (slowpath(newisa.isDeallocating())) {
ClearExclusive(&isa.bits);if (sideTableLocked) {ASSERT(variant == RRVariant::Full);sidetable_unlock();}if (slowpath(tryRetain)) {return nil;} else {return (id)this;}
} uintptr_t carry; newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowedif (variant != RRVariant::Full) {ClearExclusive(&isa.bits);return rootRetain_overflow(tryRetain);}// Leave half of the retain counts inline and// prepare to copy the other half to the side table.if (!tryRetain && !sideTableLocked) sidetable_lock();sideTableLocked = true;transcribeToSideTable = true;newisa.extra_rc = RC_HALF;newisa.has_sidetable_rc = true;
} } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
if (variant == RRVariant::Full) { if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); } else { ASSERT(!transcribeToSideTable); ASSERT(!sideTableLocked); }
Release
同Retain类似,当计算为0时,发送消息dealloc
if (performDealloc) {((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));}
Dealloc

