一:iOS内存分区情况
(1)bss区:用于存放程序中 未初始化的全局变量以及静态变量的一块内存
(2)数据区:分为 只读数据段 和读写数据段。通常用来存放程序中已经初始化的全局变量以及静态变量的一块内存。
(3)代码区:用来存放程序执行代码的一块内存区域。区域大小通常在程序运行前就已经确定。
(4)堆区:存放进程中被动态分配的内存段
(5)栈区:存放程序临时创建的局部变量
二:引用计数
1.引用计数是什么
引用计数是管理对象生命周期的一种方法。持有对象,引用计数+1;销毁对象,引用计数-1.
2.引用计数如何存储
(1):对象是否是Tagged Pointer对象;若是,则会将对象的指针值作为引用计数返回。
(2):若对象不是Tagged Pointer对象,则检查对象启用了Non-pointer;若已启用,则引用计数存在isa 中,计数超过255将附加SideTable辅助存储。
(3):对象未启用Non-pointer。使用SideTable(散列表)来储存。
3.强引用与弱引用
(1)强引用持有对象; 当前对象的生命周期 被 是否由其他对象引用限制。只有当引用计数为0的时候,对象才会被销毁。OC里的引用默认都是强引用。
(当两个对象互相持有的时候,会发生循环引用,因此就需要用弱引用来解决这一问题)
(2)弱引用不持有对象;当前对象的生命周期不被 是否由其他对象引用限制。
三:属性修饰符
1.属性修饰符都有哪些
assign/strong: 属性不声明,默认是strong.
copy/weak:
Nonatomic/atomic:nonatomic非原子性,访问速度快,非线程安全; atomic原子性,访问速度慢,线程安全(有线程锁)
readwrite/readonly:只写/只读
其中 strong/copy 是强引用; weak/assign是弱引用
2.Assign/weak 区别? 为何delegate不用assign修饰?
区别:
(1)weak只能修饰对象类型;assign既能修饰对象类型也能修饰基本数据类型
(2)weak修饰的对象,在销毁时会自动赋值为ni;而assign修饰的对象类型不会自动赋值为nil,就会造成野指针错误.(当assign修饰的基本数据类型时,数据会分配到栈空间,而栈空间是由系统自动管理分配以及释放的)
原因:
weak修饰的对象在销毁后,会自动的把它的指针置为nil; 而assign不会,对象已经被销毁,但指针还指向它,这就成了野指针
3.Strong/copy 的区别?
(1)修饰可变类型: 使用strong。
(2)修饰不可变类型:如果需要防止值不被修改,就用copy,其他情况既可用copy也可用strong。
可变类型不用copy修饰的原因,就是因为可变类型copy后,相当于进行了一次深拷贝,会开辟新的内存,会增加内存消耗。因此可变类型一般使用strong修饰。
4.Block使用copy和strong修饰有什么区别?
(1)在MRC下,block使用copy修饰。 因为block申明在栈区,使用copy修饰会复制一份到堆区。而使用strong修饰block还是栈区。栈区的特点就创建是对象随时可能被销毁,对象被销毁后还在调用就会导致程序崩溃。
(2)在ARC下,block使用copy跟strong修饰都可以,因为就算是使用strong修饰,系统会默认帮我们进行copy操作,因此在arc下,使用两者修饰没有区别。
5.深拷贝与浅拷贝
浅拷贝:也叫指针拷贝。内容相同,指针相同。
深拷贝:也叫内存拷贝。内容相同,指针不同。拷贝的对象和原对象没有任何关系。
copy: 对于可变对象为深拷贝,对于不可变对象为浅拷贝
mutableCopy:始终是深拷贝
四.变量修饰符
1.常见的变量修饰符有哪些
__weak,__strong,__block
2._block 与_weak 的区别
(1) _block不管是ARC还是MRC模式下都可以使用,可以修饰对象类型跟基本数据类型。
(2) _weak只能在ARC模式下使用,只能修饰对象类型。
(3) _block对象可以在block中被重新赋值,_weak不可以。
五:自动释放池
1.什么是自动释放池
自动释放池是oc提供的一种自动回收的机制,具有延迟释放的特性,即当我们创建了一个对象,并把他加入到了自动释放池中时,他不会立即被释放,会等到一次runloop结束或者作用域超出{}或者超出[pool release]之后再被释放。
2.自动释放池(AutoReleasePool)的数据结构与工作原理
自动释放池是由AutoReleasePoolPage构成的双向链表,每张链表头尾相接,有`parent`、`child`指针;每创建一个池子,会在首部创建一个`哨兵`对象,作为标记;最外层池子的顶端会有一个`next`指针。当链表容量满了,就会在链表的顶端,并指向下一张表。
3.ARC下为何还需要显式使用autoreleasepool
当一个对象的引用计数为0的时候,需要等到当前runloop结束的时候,才会被释放。而在当前runloop结束之前,可能会出现无数个等待被释放而没有被释放的对象,这时候内存占用率就会比较高。恰当的使用@autoreleasepool可以及时释放这些对象,降低内存的使用率。
4.autoreleasepool的使用场景
(1)遍历创建多个临时对象
(2)使用非cocoa创建的线程
六:循环引用
1.哪些场景会发生循环引用,怎么解决?
(1)delegate。 解决方案: 使用weak修饰
(2)block。 解决方案:使用__weak/__strong 修饰self对象
(3)NSTimer。 解决方案:手动nil
七:内存管理进阶之原理性问题
1.weak 工作原理
Runtime维护了一个weak表,weak表也是一张hash(哈希)表,被`weak`修饰的指针变量所指向的地址是`key`,所有指向这块内存地址的指针会被添加在一个数组里,这个数组是`Value`。当对象被dealloc释放后,dealloc方法内部会调用清空的相关函数,在weak表中根据当前对象的指针找到对象所在的弱引用数组,将数组中的弱引用指针都置为nil。
weak 的实现原理可以概括一下三步:
(1)、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
(2)、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
(3)、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
详情参考:https://www.jianshu.com/p/13c4fb1cedea
2.Dealloc工作原理
(1)首先调用_objc_rootDealloc 方法
(2) 再调用rootDealloc方法
(3) 判断对象是否可以释放。判断依据如下:
a:是否采用了优化的isa计数方式 / b:没有被弱引用 / c:没有关联对象 / d:没有自定义的C++析构函数 / e:没有用sideTable做引用计数
(4) 没有以上条件就调用free方法将对象释放;否则调用object_dispose 做下一步处理。
object_dispose 调用流程如下:
(4.1)直接调用 objc_destructInstance
objc_destructInstance调用流程如下:
(4.1.1)判断有没有C++析构函数,如果有调用object_cxxDestruct 销毁c++相关内容
(4.1.2)再判断有没有关联对象,如果有先移除关联对象
(4.1.3)调用clearDellocating方法
clearDeallocating调用流程如下:
(4.1.3.1)执行sideTable_clearDellocating
(4.1.3.2)执行`weak_clear_no_lock`,在这一步骤中,会将指向该对象的弱引用指针置为`nil`。
(4.1.3.3)接下来执行`table.refcnts.eraser()`,从引用计数表中擦除该对象的引用计数。
(4.2)调用free方法,释放资源。
dealloc流程图
网友评论