性能优化-内存管理2
MRC&ARC
ARC是LLVM和Runtime配合的结果
ARC中禁⽌止⼿手动调⽤用retain/release/retainCount/dealloc
ARC新加了了weak、strong属性关键字
alloc出来的引用计数是多少?
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
NSLog(@"%lu",(unsigned long)[p retainCount]);
}
return 0;
}
打印出的结果是1
这个时候 我们去查看retainCount的底层
RetainCount
全局搜索retainCount
![](https://img.haomeiwen.com/i2056006/ef015fad1ae9b96d.png)
![](https://img.haomeiwen.com/i2056006/19bc517e8e399be8.png)
由retainCount源码看出返回的rc是isa.bits.extra_rc + 1所得到的 再次 因为打印出来的结果是1 那么就认为isa.bits.extra_rc = 0 也就是该对象的引用计数在alloc之后是0
引用计数在什么时候会加 什么时候会减
Retain
全局搜索retain
![](https://img.haomeiwen.com/i2056006/f5beb4c66e90cb7d.png)
![](https://img.haomeiwen.com/i2056006/89e0810c66b800f8.png)
![](https://img.haomeiwen.com/i2056006/89773773f2587e33.png)
![](https://img.haomeiwen.com/i2056006/4f21367ad0f2b000.png)
引用计数表的前两位存的不是引用计数 第一位存的是是否为弱引用的标识 第二位存的是是否正在deallocating
Release
当引用计数为0的时候 会走析构方法 这个是必要条件 不是充分条件
全局搜索release
![](https://img.haomeiwen.com/i2056006/ef4f668d5740b957.png)
![](https://img.haomeiwen.com/i2056006/814d66ecd07aedcf.png)
![](https://img.haomeiwen.com/i2056006/de649580af58833a.png)
如果走析构函数 就会给这个对象发送dealloc消息
release是先对isa.bits.extra_rc进行减一操作 如果没有下溢出的话就结束
如果有下溢出的话 就去查看散列表是否存放了引用计数 如果有的话 就取出散列表中存放的那一半空间的引用计数进行减一操作然后存放到extra_rc中 结束
如果散列表中没有存放引用计数 就发送dealloc消息
为什么当引用计数超的时候 放一半到散列表?
1 当extra_rc都存放满的时候 如果全部放到散列表中 等下次extra_rc满了 再次放到散列表 会造成当对这个对象进行release操作的时候 由于extra_rc没有值的话 就会对散列表进行操作 造成对散列表的操作过于频繁 从而影响到性能
2 一半正好是位移操作的一个位置 便于直接简单操作 左移右移一位 能够直接定位
3. 不大不小 正好合适 对于retain release来说
retain 相应的引用计数加1
release 相应的引用计数减一
dealloc应该做一些什么事情
weak
![](https://img.haomeiwen.com/i2056006/4956c227ee2bc231.png)
全局搜索objc_initWeak
![](https://img.haomeiwen.com/i2056006/565b2ce8796aa0a0.png)
如果弱引用表中有weak对象 就remove
![](https://img.haomeiwen.com/i2056006/6afedb4b6b046d3c.png)
![](https://img.haomeiwen.com/i2056006/b664e2a2fe13f1c4.png)
![](https://img.haomeiwen.com/i2056006/45da51ac04e62441.png)
如果弱引用表中没有weak对象
1.就进行weak_register_no_lock操作
![](https://img.haomeiwen.com/i2056006/37e2aa6caae54d42.png)
2.创建entry
@如果weak_entries不存在的话 就直接返回
如果存在的话 就找到那个原先没有存放过entry的位置 返回这个位置的entry
![](https://img.haomeiwen.com/i2056006/0674171010946118.png)
如果这个entry是存在的话 就执行append_referrer(entry, referrer);方法
在这个方法中 就是把这个弱引用的指针存放在entry的inline_referrers数组,同时要把这个弱引用的指针存放在new_referrers数组里面 也要赋值给entry的referrers变量
3.如果entry无法创建的话
![](https://img.haomeiwen.com/i2056006/e4739c66920cdfb4.png)
@新建一个entry
@如果weakTable的entrys里面存放的值的大小的3/4已经达到了weakTable设置的num_entryies的大小 那么久会扩容
![](https://img.haomeiwen.com/i2056006/f2d5d7bbcfb70d77.png)
@然后把entry加入到weak_entries里面 同时num_entries进行加一操作
![](https://img.haomeiwen.com/i2056006/6488a7f6bec74cba.png)
有一步操作需要注意: 在新建entry的时候对他的inline_referrers数组进行了初始化 把weak对象和entry联系起来了
![](https://img.haomeiwen.com/i2056006/c8d983b01626495f.png)
weak对引用计数不操作
![](https://img.haomeiwen.com/i2056006/4430b158ac5f4f9c.png)
1:拿到sideTable
2:得到sideTable的weakTable 弱引⽤用表
3:创建⼀一个weak_entry_t
4:把referent加⼊入到weak_entry_t的数组inline_referrers
5:把weak_table扩容⼀一下
6:把new_entry加⼊入到weak_table中
dealloc
全局搜索dealloc
![](https://img.haomeiwen.com/i2056006/02a11575f486877d.png)
如果他没有弱引用 没有关联对象 没有c++函数 散列表里面没有放引用计数的话 直接free 否则 object_dispose
![](https://img.haomeiwen.com/i2056006/8d6618712be8b5c2.png)
如果有C++函数 或者如果有关联对象 直接remove 然后再执行clearDeallocating
![](https://img.haomeiwen.com/i2056006/33d2af7bc62ff676.png)
![](https://img.haomeiwen.com/i2056006/65beccf26b5e8f9f.png)
![](https://img.haomeiwen.com/i2056006/e22d865c7e6d6b5c.png)
![](https://img.haomeiwen.com/i2056006/20de41708ba94737.png)
![](https://img.haomeiwen.com/i2056006/a4283258c12f9311.png)
![](https://img.haomeiwen.com/i2056006/6694875b786c39fc.png)
1:根据当前对象的状态是否直接调⽤用free()释放
2:是否存在C++的析构函数、移除这个对象的关联属性
3:将指向该对象的弱引⽤用指针置为nil
4:从弱引⽤用表中擦除对该对象的引⽤用计数
Strong
全局搜索objc_storeStrong
retain新值 release旧值
![](https://img.haomeiwen.com/i2056006/c090b347edb2369c.png)
objc_ivar_memoryUnretained不是指针的传递 是对象的传递 也就说value为nil的话
*location 就是野指针 浪费空间 这就是unsafe不安全的原因
![](https://img.haomeiwen.com/i2056006/471121fd85dc531d.png)
自动释放池
自动释放池: 容纳变量 在合适的位置释放
在何时使用?
1.大量的临时变量
2.非UI操作 命令行代码
3.自己创建辅助线程
查看cpp代码
1.cd 到main存在的目录
2.终端执行clang -rewrite-objc main.m -o main.cpp
3.打开main.cpp
struct __AtAutoreleasePool {
//产生一个atautoreleasepoolobj对象
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
//析构函数
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
全局搜索objc_autoreleasePoolPush
![](https://img.haomeiwen.com/i2056006/f599cd61cac918f8.png)
autoreleasePoolPage 属性占用的字节为56个
![](https://img.haomeiwen.com/i2056006/3ab14fb375fb4dc3.png)
![](https://img.haomeiwen.com/i2056006/74a3efb7e74090f8.png)
![](https://img.haomeiwen.com/i2056006/8c08dba08ceb0179.png)
一页所能容纳的大小 是 4096
![](https://img.haomeiwen.com/i2056006/ec18f3a43bad8aec.png)
![](https://img.haomeiwen.com/i2056006/8dd98ec133f8e13e.png)
![](https://img.haomeiwen.com/i2056006/079a6eafe827bc6a.png)
![](https://img.haomeiwen.com/i2056006/6e86432845e4a7b7.png)
![](https://img.haomeiwen.com/i2056006/e4f697684f049115.png)
一共可以容纳4096个字节 减掉本身占用的56个字节 剩下4040个字节 每个变量占用8个字节 也就是可以容纳505个
全局搜索_objc_autoreleasePoolPrint
![](https://img.haomeiwen.com/i2056006/c0b0622f9f9a7a72.png)
![](https://img.haomeiwen.com/i2056006/61ab5217687a428b.png)
![](https://img.haomeiwen.com/i2056006/df496d35c726c4b8.png)
4096个大小 有56个属性 进行压栈之前要开辟一个边界符然后继续压栈 直到这个页面满了之后 开辟新的页面 还是要先开辟一个边界符
autorelease
全局搜索autorelease {
![](https://img.haomeiwen.com/i2056006/9ecf6ef7f74ced1a.png)
直接执行autoreleaseFast方法
![](https://img.haomeiwen.com/i2056006/15a78c4febdf9b72.png)
![](https://img.haomeiwen.com/i2056006/8232976ed936f6de.png)
Push
1.有页面但是满了 - 创建新的页面 老页面的child指针指向新的页面 新界面的parent指针指向老页面
2.有页面但是没有满 直接加入进去
3.一个页面都没有 创建新的界面
都执行add(objc) *next++ = objc
pop
全局搜索pop
![](https://img.haomeiwen.com/i2056006/af48107046fc6855.png)
![](https://img.haomeiwen.com/i2056006/17fddfb55389cbc4.png)
id obj = *--page->next;
都执行release(objc)
![](https://img.haomeiwen.com/i2056006/69cdd3c5d07dbcec.png)