iOS 内存管理
一 . 内存管理 包括内存分配 和 内存清除
1.内存管理的范围 :人和继承于NSObject类的对象都需要进行内存管理,任何非对象类型的对象(基本数据类型 如 int char float double struct enum等)
2.为什么只有OC对象才需要进行内存管理?
OC对象放在堆里面 非OC对象(基本数据类型 如 int char float double struct enum等)一般放在栈里面,栈内存会被系统自动回收
3.堆和栈
栈(操作系统):有系统自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈(先进后出)
堆(操作系统):一般由程序员手动分配释放,如果程序员不释放,程序结束时就会被系统回收,分配方式类似于链表
4.引用计数器
每个OC对象都有自己的引用计数器 从字面上可以理解成”对象被引用的次数” 也可以理解为有多伤风人正在使用这个对象
引用计数器的作用:引用计数器表示当前有多少人正在使用这个对象 到没有任何人在使用这个对象时系统就会回收这个对象 也就是说当对象的引用计数为0时,对象占用的内存就会被系统回收.如果系统的引用计数不为0那么在整个程序运行过程中,他占用的内存就不可能被回收(除非整个程序以经推出)
任何一个对象在刚创建的使用引用计数都为1 当使用 alloc init copy 等创建对象时,对象的引用计数默认为1
5.如何操作引用计数器
给对象发送一个 retain 消息 可以使引用计数器的值 +1
给对象发送一个 release 消息 可以使引用计数器的值 -1 ,release 并不代表销毁\回收对象 仅仅是引用计数值 -1
给一二对象发送 retainCount 消息 可以得到当前对象的引用计数器值 有时候不准确 很少用 因为常出错
需要注意的是: release 并不代表销毁\回收对象 仅仅是引用计数值 -1 只有当引用计数器值减到0 就会释放
6.dealloc方法
当一个对象的引用计数器为0时,这个对象即将被释放,其占用的内存被系统回收
对象即将被销毁时系统会自动给对象发送一条dealloc消息(因此,从dealloc方法有没有被调用.就可以判断对象是否被销毁)
dealloc函数是系统调用的函数.不可程序员直接调用.一般情况下,会重写dealloc方法来释放相关资源,重写的dealloc方法就相当于对象的遗言,用来处理对象销毁后的一些后事.一旦重写了dealloc方法,就必须调用 [super dealloc] 并且放在dealloc方法的最后调用.
注意事项:dealloc方法是系统调用的方法,程序员不可直接调用,需要重写dealloc方法.一旦对象被回收,它占用的内存就不可再用,如果坚持使用就会导致程序崩溃(野指针错误)
二 .MRC(Manual Reference Counting) :所有的对象需要程序员手动管理,需要程序员自己编写release/retain等方法
1.内存管理的原则 :有加就有减 一次alloc就有一次release 一次retain就有一次release
2.野指针 空指针 僵尸对象
僵尸对象 : 只要一个对象引用计数为0 被释放 就称这个对象为僵尸对象
野指针 : 当一个对象指向僵尸对象,我们就成这个指针为野指针 (一个指针指向的对象的引用计数值为0 则这个指针为野指针).只要给野指针发送消息就会报错 EXC_BAD_ACCESS
企业开发中 一定要开启对僵尸对象的监听
空指针 : 为了避免给野指针放消息会报错,当一个对象被释放后我们会将这个对象设置成空指针nil/0. 因为给空指针发送消息不会报错.
3.多对象的内存管理
如果要在A对象和B对象中用C对象 一定要在A对象和B对象中对C对象进行一次retain 当A和B用完后也要对C进行release 一般情况下,A和B对C的retain放在A和B对C的调用函数中,A和B对C的release放在dealloc方法中.其余的A B C 的自释放都在原来的主函数中.
就像好几个人进一个游戏大厅打游戏,每进去一个人都要让大厅记录+1,当有人离开时,大厅里人数就会-1.当大厅里人数纪录为0时,就大厅释放.只要还有人,哪怕是一个人也不能释放大厅.
4.@property修饰符
readonly:只生成getter方法
readwrite:同时生成getter/setter方法,默认情况下不写就是readwrite
getter:可以给生成的gutter方法起名称
setter:可以给生成的setter方法起名称
retain:会自动帮我们生成setter/gettter方法内存管理的代码
assign:不会替我们生成setter方法的内存管理方法,仅仅只是生成普通的gettter/setter方法,默认什么都不写就是assign
5.@class
@class 可以简单地引用一个类,就是告诉系统这是一个类,可以放心大胆的使用.
具体使用中,在 .h 文件中使用 @class, 在 .m 文件中使用 #import包含这个类的 .h 文件. @class 不拷贝这个类只是告诉编译器这是一个类,可以使用. #import 会拷贝累的头文件.
在用到 @class 时,在 .h 文件里 @class,同时在 .m 文件里也要 #import,不可省略.
对于循环以来的关系使用@class可以避免死循环,因为 .h 文件中的 @class 不做任何拷贝, .m 文件中的 #import 只会拷贝对应的文件,不会形成死循环.
6.循环retain
如果A对象要拥有B对象,且B对象也要拥有A对象.此时就会形成循环retain
解决办法:让其中一方不要做retain操作
7.autorelease
只要给一个对象发送autorelease消息,就会将对象放到一个自动释放池中,当自动释放池销毁时,会对池子里面的所有对象做一次release操作.
注意:这里只是发送autorelease消息,如果当时的引用计数值为0,则对象销毁,否则对象不会被释放. 调用完autorelease方法后,对象的引用计数值不会改变
autorelease的原理:autorelease实际上只是把对release的调用延迟了,对于每个autorelease系统只是把对象放到当前的自动释放池中,挡自动释放池被释放时,该吃中的所有对象都会被release
三.ARC(Automatic Reference Counting) : 不需要程序员管理内存,编译器会自动给程序议案家release/retain等代码
1.注意点
iOS中的ARC和java中的垃圾回收机制不同.iOS中的ARC是编译器在编译阶段自动识别添加release/retain等方法带代码中在执行代码;java中的垃圾回收机制是系统在运行程序是定时检测垃圾并回收,是系统做的.
2.ARC的判断原则
使用ARC时只要还有一个强指针变量指向对象,对象就会保持在内存中
强指针:被__strong修饰的指针,如果没有修饰词,默认的情况下都是强指针,强指针指向的对象会一直保存在内存中
弱指针:被__weak修饰的指针 ,弱指针指向的对象在创建完成后会立即被释放,无法保存
3.ARC中的循环引用
在ARC中如果A拥有B,B拥有A,一个用strong 一个用weak,必须有一方使用弱指针
4.ARC中@property参数
strong : 用于OC对象,相当于MRC中的retain
weak : 用于OC对象,相当于MRC中的assign
assign : 用于基本数据类型,相当于MRC中的assign