iOS 内存管理

一.内存布局以及结构

1.1内存布局
内存布局.png
stack(栈区):由编译器自动分配释放,存放函数的参数值,局部变量等。
heap(堆区) :通过alloc等分配的对象,一般由程序员分配释放。
bss 未初始化的全局变量等
data 已初始化的全局变量等
text 程序代码
1.2内存管理方案

内存管理方案有3种:
TaggedPointer (小对象,nsnumber等)
NONPOINTER_ISA
散列表(包含弱引用表 和 引用计数表)

散列表方式1.png
散列表方式2.png
分离锁.png

question:为什么不是一个SideTable,而是多个SideTable组成一个SideTables数据结构?

如果只有一个SideTable,相当于内存中所有对象的引用计数或弱引用都在一个表里,如果操作引用计数进行修改。多个线程进行操作需要进行加锁处理。就会有效率问题。为了解决这个问题,系统引入了分离锁的方案。

怎样实现快速分流?
通过Hash表(Side Tables的本质是一张Hash表)。

1.3引用计数管理

alloc实现

经过一系列调用,最终调用了C函数的calloc
此时并没有设置引用计数为1

retain实现

retain实现.png

release实现

release实现.png

retainCount实现

retainCount实现.png

dealloc实现

dealloc实现.png

1.4自动释放池

image.png
image.png
image.png
二.常见问题
1.MRC和ARC的区别?

MRC
手动引用计数管理对象的内存

ARC
自动引用计数管理对象的内存
ARC是LLVM(编译器)Runtime协作来进行自动引用计数的内存管理。
禁止手动调用retain/release/retainCount/dealloc
ARC中新增weak,strong属性关键字。

系统是怎样将一个weak变量添加到弱引用表中的?

被声明为__weak的对象指针,经过编译器编译后会调用objc_initWeak()方法,经过一系列函数调用,最终会在
weak_register_no_lock()函数中进行弱引用变量的添加。具体添加的位置是通过哈希算法进行位置查找的。

2.为系统是怎样将一个weak变量添加到弱引用表中的?
被声明为__weak的对象指针,经过编译器编译后会调用objc_initWeak()方法,经过一系列函数调用,
最终会在weak_register_no_lock()函数中进行弱引用变量的添加。具体添加的位置是通过哈希算法进行位置查找的。

如果查找对应位置中有 当前对象所对应的弱引用数组,我们把新的弱引用变量添加到数组中。
没有的话,我们重新创建一个弱引用数组,把第0个位置添加最新的weak指针,后面的都初始化为nil。
3.为什么weak指针指向的对象在废弃之后会被自动置为nil?
Runtime会对weak属性进行内存布局,构建hash表:以weak属性对象内存地址为key,
weak属性值(weak自身地址)为value。当对象引用计数为0 dealloc时,
会将weak属性值自动置nil。
当一个对象被dealloc之后。在dealloc内部实现当中。会调用弱引用清除的相关函数,
然后在函数内部实现当中 ,会根据对象指针查找弱引用表,
把当前对象相对应的弱引用都拿出来放到一个数组中,遍历数组当中的弱引用指针,分别置为nil。
4.autoreleasepool的实现原理是:

为节点,通过双向链表形式组合而成的数据结构.
是和线程一一对应的

5.如何破除循环引用?

1.避免产生循环引用
1.1 __weak
1.2__block

__block破解:
MRC下,__block修饰对象不会增加其引用计数,避免了循环引用。
ARC下,__block修饰对象会被强引用,无法避免循环引用,需手动解环。

1.3__unsafe_unretained

修饰对象不会增加引用计数,避免了循环引用
如果被修饰对象在某一时机被释放,会产生悬垂指针。

2.在合适的时机手动断环

6.为什么GCD,masonry都没有循环引用问题?

因为self并没有对他们的block进行持有。

你可能感兴趣的:(iOS 内存管理)