什么是内存管理?
一句话概括就是通过对有限的内存资源合理分配使用,并及时清理,来保证堆空间能被正确地释放,从而保证程序的高效运行。OC的内存管理,增加了一个引⽤计数器的概念,当我们使用alloc,retain,new,copy,mutableCopy去创建对象或引⽤对象的时候,该对象的引⽤计数+1,那么对应的必须对该对象进行release操作,使引⽤计数-1,当引用计数为0的时候,该对象才会真正的被销毁掉。
内存管理机制为什么要进行内存管理?
我们的移动设备的内存是有限的,每个App所能占用的内存也是有限的,当App所占用的内存较多的时候,系统会发出警告,这时就必须要销毁一些不需要的内存空间,但如果系统自动回收内存后,还是占用太多内存,App就会闪退,那么这对开发者来说算是一个巨大的严重BUG,但是内存管理并不能针对所有的数据类型,只有继承了NSObject任何对象才能进行内存管理,对其他基础数据类型(如int、char、float、double、bool、struct、enum)是无效的,因为这些基础数据类型是放在栈区的,并由编译器自动分配和释放回收,而对象分配的内存是在堆区的,堆区的内存一般是需要开发者手动分配释放的,如果开发者不主动释放,就会造成内存泄漏,所以OC中alloc都是存放在堆中的。
那么在进行内存管理时,我们还要注意以下问题:内存泄漏、提前释放和重复释放。
内存泄露:如果堆空间没有释放,就会造成内存泄露
提前释放:释放了还需要使⽤的内存空间,那就是提前释放
重复释放:同一个内存空间被释放多次
OC中的内存管理ARC(Automatic Reference Counting)和MRC(Mannul Reference Counting)
切换RC环境 古老操作ARC自动内存管理:当给一个对象发送autorelease消息的时候,就会将这个对象加入到一个自动释放池中,当自动释放池销毁时,就会给池子里的所有对象发送一条release消息。
MRC手动内存管理:手动管理对象的retainCount的值,当retainCount的值为0时,对象就会被销毁释放掉。
虽然开发者用ARC的多,但是ARC并不是完美的,在使用ARC时,可能会因为代码的不规范,导致内存提前释放,所以了解MRC也是有必要的,毕竟某些第三方库或者旧文件的代码可能是不支持ARC的,况且还是有个别的奇葩企业是非常推崇手动内存管理的,目前我还没遇到,但是面试一定会遇到。
MRC(Mannul Reference Counting)
既然是手动内存管理,肯定会有人困惑,在手动内存管理时如何确保内存被正确释放,当我们要释放一个堆空间时,如何确保这个堆没有对象使用它,保证都访问完毕,避免提前释放,当释放指针指向的堆空间,怎样确定哪些指针指向了同一个堆,并释放一次这些指针,避免重复释放。
为了解决以上的这些问题,OC引出了引⽤计数器的概念:
1.每个对象都有⾃己的引⽤计数器,是一个整数,表示”对象被引用的次数”,即由多少对象正在使⽤这个OC对象
2.每个OC对象的内部专门有4个字节的存储空间来存储引用计数器
引用计数器的作用:
1.当我们使⽤alloc,new或者copy等创建⼀个新对象的时候,新对象的引用计数器默认为1,当给对象发送⼀条retain消息,可以使retainCount(引用计数器的值)+1,retain⽅法则会返回对象本⾝;给对象发送一条release消息,可以使retainCount(引⽤计数)-1,而release是没有返回值的。
2.当一个对象引用计数器的值为0,对象占用的内存就会被系统释放回收,换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被销毁,除非整个程序退出。
对象的销毁
当一个对象的引用计数器的值为0时,那么它将会被销毁释放掉,其占用的内存被系统回收,系统也会自动地给对象发送一条dealloc消息,而一般情况下我们都会重写dealloc方法,并在这个方法里释放相关资源,dealloc就好比对象的最后遗言,但是一旦重写了dealloc方法,就必须调用父类的dealloc方法[super dealloc],并且要放在最后调用,同样也不要直接手动去调用dealloc方法,一旦对象被回收了,它占用的内存就不可以再用,坚持使用可能出野指针错误导致程序崩溃。
所以在对象销毁的时候,我们还要注意僵尸对象、野指针和空指针的问题:
僵尸对象:所谓僵尸对象就是所占内存已经被回收的对象,僵尸对象不能再使用
野指针:指向僵尸对象的指针,其实指向的是一块不可用的内存,给野指针发消息就会报错
空指针:并不是野指针,是没有指向任何对象的指针,存储的是nil、NULL或布尔值0等,但是给空指针发送消息不会报错,也没有任何响应,相当于放空炮
另外提一句,release对象并不是释放对象,而只是使对象的引用计数-1,只有当对象的引用计数为0时,系统才会释放回收对象所占的内存,并自动dealloc。
内存管理的黄金法则
1.凡是用alloc,retain,new,copy(mutalbleCopy)方法或者是用他们开头的方法创建的对象,都必须使⽤release或者autorelease方法释放;
2.谁创建的对象,谁释放这个对象(哪个类创建,哪个类释放),谁alloc,谁release。
自动释放池(autorelease pool)
自动释放池就好比是一个容器,当对一个对象发送autorelease消息,会把这个对象加入到自动释放池里边,当池⼦销毁的时候,会对池子里边的每个对象发送一次release消息,达到对象延迟释放的作⽤。正因为自动释放池会延迟释放,所以在手动内存管理中能不使用autorelease就不使用autorelease,否则可能会造成内存的(延迟积累)积累。autorelease不会使对象的引用计数-1,只有当自动释放池释放的时候,才会使对象的引用计数-1,一个工程中并不是只有一个自动释放池,可以有多个自动释放池,因为每一个触发周期都会创建一个自动释放池。
一般情况下,在一些需要返回对象的方法里,因为要返回对象,所以不能立马执行release操作,否则会返回一个已释放的对象,这个时候使用autorelease把对象加入到自动释放池,利用延迟释放的特点,等自动释放池销毁的时候,会对释放池里的对象发送release消息,使对象的引用计数-1,避免提前释放的问题。
@property参数
retain:用于OC对象,在给旧值赋新值时,要release旧值,retain新值,避免出现内存泄漏
assign :用于非OC对象,有默认值,能直接赋值,但不做任何内存管理,比如int、bool类型
copy:要release旧值,copy新值,(一般用于NSString *)
atomic:原子性的,单线程操作,容易形成自锁,性能低(声明的属性默认为atomic)
nonatomic:非原子性的,多线程操作,性能高
readwrite:可读可写,同时生成setter和getter方法(声明的属性默认为readwrite)
readonly:只读,只会生成getter方法
网友评论