iOS晋级知识汇总(八)内存管理

2019-07-11  本文已影响0人  struggle3g

iOS的内存管理

内存布局

内存分区.png

内存管理方案

内存管理的分析全部都是基于开源代码的讲解

NONPOINTER_ISA

arm64结构下是64位

散列表方式

SideTable

散列表方式.png

为什么不是一个SideTable? 为什么要组成SideTables呢?

怎么样实现快速分流?

SideTables的本质是一张Hash表。
可能有64个SideTable存储不同的引用计数表、和弱引用表

什么是hash算法

对象指针可以作为Key通过hash函数计算计算出hash表中你的value值的下标也就是索引。

hash查找的过程

给定值是对象内存地址,目标是数组下标索引。

给了一个对象的内存地址,通过哈希函数的运算,得到了一个集合的下标索引值

对象的内存地址指针 % 数组的count就可以得到这个数组中的索引

这样就查找速度快了

散列表方案设计的数据结构

引用计数表

弱引用表

MRC和ARC

MRC

手动引用计数,进行内存管理

ARC

什么事ARC

全名自动引用计数

ARC和MRC的区别

引用计数管理

实现原理分析

alloc实现

retain实现

  1. SideTable &table = SideTables()[this] 先获取对应的SideTable
  2. size_t & refcntStorage = table.refcnts[this]; 获取到SideTable中的引用计数值
  3. refcntstorage += SIDE_TABLE_RC_ONE; 对其+1
    • SIDE_TABLE_RC_ONE不是1,前两个位置是存储弱引用计数,和是否正在dealloc的,是后面62位,所以需要做一个偏移量。 所以说是4.

release实现

  1. SideTable &table = SideTables()[this] 先获取对应的SideTable
  2. RefcountMap:iterator it = table.refcnts.find(this); 获取到SideTable中的引用计数值
  3. it->second -= SIDE_TABLE_RC_ONE; 和retain正好相反

retainCount的实现

  1. SideTable &table = SideTables()[this] 先获取对应的SideTable
  2. size_t refcnt_result = 1; 声明一个局部变量
  3. RefcountMap::iterator it = table.refcnts.find(this) 获取到SideTable中的引用计数值,刚去alloc的对象其实里面是为0的
  4. refcnt_result += it-> second >> SIDE_TABLE_RC_SHIFT 把查找的结果做一个向右偏移的操作,然后在+1

dealloc的实现

dealloc的内部实现.png

object_dispose实现

dealloc内部实现中dispose函数.png

objc_destructInstance的实现

objc_destructInstance函数实现.png

clearDealloccating的实现

clearDealloccating.png

弱引用管理

弱引用调用栈

弱引用调用栈.png

当清除weak变量 通知设置指向为nil, 它是如何置nil的

如何将weak自动置为nil.png

完整过程

总结

自动释放池

自动释放池的面试题.png

编译器改写

objc_autoreleasepoolPush和objc_autoreleasePoolPop的函数调用栈

自动释放池的数据结构

AtuoreleasePoolPage的数据结构

AtuoreleasePoolPage::Push

AutoreleasePoolpagePush操作的原理.png

autorelease

autorelease的实现过程.png

AtuoreleasePoolPage::Pop

AutoreleasePoolpagePop的原理操作.png

双向链表的概念

双向链表的结构.png

栈结构概念

栈结构.png

面试题自动释放池的实现结构是什么?

自动释放池的总结

下面代码什么时候释放

viewdidload{
    NSMutableArray *array = [NSSmutableArray array];
}

多次嵌套就是多次插入哨兵对象

关于autoreleasepool为和可以嵌套调用?

实际上多次嵌套插入哨兵对象,每次进行autoreleasePool的代码块创建的时候,假如autoreleasePoolpage没有满的情况下,系统就会在当前autoreleasePoolpage栈中进行一次哨兵对象的插入,如果满了会创建一个新的autoreleasePoolpage,然后当前的autoreleasePoolpage中的child指针指向新创建的autoreleasePoolpage。

所以autoreleasepool可以多层嵌套调用

循环引用

三种循环引用:

自循环引用

自循环引用.png

相互循环引用

相互循环引用.png

多循环引用(大环)

大环循环引用(多循环引用).png

如何破除循环引用?

具体解决方案:

__weak 破解循环引用

__weak避免产生循环引用.png

__block破解* 破解循环引用

__unsafe_unretained 破解循环引用

循环引用示例

在开发过程中,你遇到过哪些循环引用问题,你又是如何解决的?

有一个页面有一个banner滚动栏,每3秒滚到一次。一般有一个banner对象,VC对它强持有,
banner对象的需求是每3秒滚动一次。需要添加一个NSTimer,当我向banner对象添加回调的时候,NSTimer会对banner对象施加一个强引用,这个时候就产生了相互循环引用问题。

实际上,NSTimer创建完成以后会有一个主线程RunLoop去强引用NSTimer强引用。就算是VC退出释放掉,那么banner对象也不会释放,因为主线程runLoop强引用了这个对象。

NSTimer有重复,以及非重复定时器,假如是非重复定时器,那么在NSTimer回调完成以后设置无效并置nil,那么就破解了循环引用。

假如NSTimer重复多次回调

第一种:在NSTimer和Banner之间设置一个中间对象,这个中间对象分别弱引用NSTimer和banner对象。那么当VC释放掉,banner对象也就释放掉了,NSTimer的回调会去中间对象,而中间对象只要判断banner对象为不为nil 如果为nil就直接无效化NSTimer。

NSTimer的循环引用问题.png

考点

面试题总结

如果我们对一个类添加了关联对象,那么在这个类被释放以后会清除掉这个关联对象吗?

会的,因为系统在dealloc中释放了关联对象

关于autoreleasepool为和可以嵌套调用?

实际上多次嵌套插入哨兵对象,每次进行autoreleasePool的代码块创建的时候,假如autoreleasePoolpage没有满的情况下,系统就会在当前autoreleasePoolpage栈中进行一次哨兵对象的插入,如果满了会创建一个新的autoreleasePoolpage,然后当前的autoreleasePoolpage中的child指针指向新创建的autoreleasePoolpage。

autoreleasePool的应用场景

在for循环中alloc图片数据等内存消耗较大的场景手动插入autoreleasePool。

autoreleasePool的实现原理是什么?

autoreleasePool实现原理:

什么是ARC

为什么weak指针指向的对象在废弃之后会被自动置为nil

当对象在被废弃之后,dealloc的内部方法实现当中会调用清除弱引用的方法,然后在清除弱引用的方法中会通过hash算法来查找被废弃对象在弱引用表当中的位置来提取它所对的人用引用指针的列表数组,然后进行for循环遍历,把每一个弱引用指针都置为nil

苹果是如何实现AutoreleasePool

AutoreleasePool是以栈为节点,双向链表形式来合成的数据结构。

什么是循环引用?你遇到过哪些循环引用,是怎么解决的?

比如说NStimer的循环引用。

上一篇下一篇

猜你喜欢

热点阅读