内存布局
- 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 release
for (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++ overflowed
if (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