iOS开发 内存管理
2021-01-17 本文已影响0人
喜剧收尾_XWX
一、内存五大区
内存五大区二、内存管理方案
iOS操作系统是针对不同场景,会提供不同的内存管理方案,
- TaggedPointer
- 对一些小对象,如NSNumber等,采用的是TaggedPointer这种内存管理方案
- NONPOINTER_ISA
- 对于64位架构下的iOS应用程序采用的是NONPOINTER_ISA这种内存管理方案
在64位架构下,ISA这个指针本身是占64个bit位的,但其实有32位或者40位就够用了,剩余的bit位其实是浪费的,苹果为了提高内存的利用率,在iSA剩余的这些bit位当中,存储了一些关于内存管理方面的相关内容,这个叫非指针型的ISA
- 散列表Side Tables
- 是一种很复杂的结构,其中包含了引用计数表和弱引用表
三、散列表Side Tables
散列表Side Tables是由多个Side Table组成,每个Side Table是由自旋锁、引用计数表、弱引用表组成。
四、信号量与自旋锁的区别
- 自旋锁是一种"忙等"的锁,如果当前锁已被其他线程获取,当前线程会不断探测这个锁有没有被释放,
如果被释放了,线程就会第一时间去获取这个锁 - 比如说其他的锁,比如信号量:当它获取不到这个锁时,会把自己的线程进行阻塞休眠,然后等到其他线程释放这个锁的时候,再唤醒当前线程
- 自旋锁适用于轻量访问。
五、ARC、MRC
如果说一个对象有weak指针指向它,当这个对象dealloc或者废弃之后,它的weak指针为何会被自动置为nil?
- 是因为在dealloc的内部实现中,有做关于它相关的弱引用指针自动置为nil的操作
六、__weak
//使用__weak关键字修饰的obj1变量指向一个通过alloc分配的一个对象obj,此时有了__weak弱引用指针
{
id __weak obj1 = obj;
}
| 代码块经过编译之后变成下面
|
{
id obj1;
objc_initWeak(&obj1,obj); //实际上是使用objc_initWeak函数,传递了两个参数(弱引用变量的地址,被修饰的对象)
1. 一个weak变量是怎样被添加到弱引用表当中的?
- 一个被声明为__weak的一个对象指针,经过编译器的编译之后呢,会调用objc_initWeak方法。
- 然后经过一系列的函数调用栈,最终在这个weak_register_no_lock函数中进行弱引用变量的添加到弱引用表。
- 添加的位置是通过一个哈希算法来进行位置查找的。
- 如果说我们查找对应位置当中已经有了当前对象所对应的弱引用数组,就把新的弱引用变量添加到那个数组当中。如果没有的话,我们就重新创建一个弱引用数组,然后把第0个位置添加上我们最新的weak指针,后面的都初始化为0/nil
2.当一个对象被废弃/释放之后,weak变量是如何处理的?
- 在调dealloc后,经过一系列的调用,在内部最终会调用弱引用清除的相关函数weak_clear_no_lock()
- weak_clear_no_lock内部会根据当前对象指针查找弱引用表,把当前对象相对应的弱引用都拿出来,是一个数组,然后遍历数组,遍历所有的弱引用指针,如果弱引用指针代表的地址就是被废弃的地址referent的话,就置为nil。
七、AutoreleasePool
1.viewDidLoad中通过NSMutebleArray的array类方法创建数组对象,这个array对象的内存是在什么时机释放的?
- 在每一次runloop循环将要结束时,会对前一次创建的AutoreleasePool进行pop操作,同时会push进来一个新的AutoreleasePool,所以在viewDidLoad中所创建的array对象,是在当次runloop将要结束的时候,调用AutoreleasePoolPage的pop方法中释放的
2.autoreleasepool{}在编译器的内部实现
//在autoreleasepool中的所有对象,都会添加到自动释放池中,当进行pop之后, autoreleasepool中所有对象都会被发送一次release消息
@ autoreleasepool {
//AutoreleasePoolPage是C++类,调用它里面的push方法
void *ctx = objc_autoreleasePoolPush(){
void *objc_autoreleasePoolPush(void)
|
void *AutoreleasePoolPage::push(void)
};
//调用AutoreleasePoolPage中的pop函数,一次pop实际上相当于一次批量的pop操作
objc_autoreleasePoolPop(ctx){
void objc_autoreleasePoolPop(void *ctxt)
|
AutoreleasePoolPage::pop(void *ctxt)
};
}
- 在当次runloop将要结束的时候调用AutoreleasePoolPage::pop()
- autoreleasePool的多层嵌套调用就是多次插入哨兵对象,当我们每次进行autoreleasePool代码块创建的时候,系统就会为我们进行哨兵对象的插入
- autoreleasePool的使用场景: 在for循环中alloc出大量的图片数据等内存消耗较大,需要在for循环内部手动插入autoreleasePool,每一次for循环,都进行一次内存的释放,来降低内存的峰值
- AutoreleasePool的实现原理以栈为结点,通过双向链表形式组合而成的一个数据结构