内存管理
- 内存管理的范围
- 任何继承了NSObject的对象都需要内存管理
- 对其他非对象类型无效(int,char,float,double)
- 为什么只有对OC对象才需要进行内存管理
- OC对象存放于堆里面
- 非OC对象一般放在栈里面(栈内存会被兄台你自动回收)
- 栈和堆
- 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等,其操作逻辑类似于数据结构中的栈,先进先出
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表
int main(int argc, const char * argv[]) { @autoreleasepool { int a = 10; // 栈 int b = 20; // 栈 // p : 栈 // Person对象(计数器==1) : 堆 Person *p = [[Person alloc] init]; } // 经过上一行代码后, 栈里面的变量a\b\c都会被回收 // 但是堆里面的Person对象还会留在内存中,因为它是计数器依然是1 return 0; }
引用计数器
- 什么是引用计数器
- 每个OC对象都有自己的引用计数器
- 它是一个整数,占4字节
- 从字面上, 可以理解为”对象被引用的次数”
- 也可以理解为: 它表示有多少人正在用这个对象
- 任何一个对象,一创建出来,引用计数器都为1
- 引用计数器的操作
- retain 加一,retain方法返回对象本身
- release 减一,并不代表销毁,仅仅减一
- reatainCount 获取当前引用计数器值,不准确
- dealloc方法
- 当一个对象的引用计数器为0时,系统就会自动调用dealloc方法
- dealloc的重写
- 一般会重写此方法,在这里释放相关资源
- 一旦重写了此方法,就必须调用[super dealloc],并且一定在最后调用
- 使用注意
- 不能直接调用dealloc方法
- 一旦对象回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
- 单个对象内存管理
- ARC:自动引用计数,不需要程序员管理内存,编译器会在适当的地放自动加上release/retain
- OC中ARC和Java中的垃圾回收机制,Java中的垃圾回收时系统干的,OC中的ARC是编译器干的
- MRC:手动引用计数,所有对象的内存都手动管理
- 将ARC变为MRC:
- 点击项目
- 在Building Setting中搜索 Automatic
- 将YES改为NO
- 内存管理原则:有加就有减
- ARC:自动引用计数,不需要程序员管理内存,编译器会在适当的地放自动加上release/retain
- 野指针和空指针
- 只要一个对象被释放了,我们就称这个对象为僵尸对象
- 当一个指针指向一个僵尸对象,我们就称这个指针为野指针
- 只要给野指针发送消息就会报错
- 在开发时要开启僵尸监听,找错误时添加全局断点
- 为了避免给野指针发送消息,一般情况下,一个对象被释放后我们会将这个对象的指针设置为空指针
- 多对象内存管理
- 只要一个对象想使用另一个对象,就需要对另一个对象的引用计数器+1
- 只要一个对象不想使用另一个对象,就需要对另一个对象的引用计数器-1
- 当A对象想使用B对象一定要对B对象进行一次retain,这样才能保证A对象存在B对象就存在,也就是说这样才能保住无论什么时候A对象都可以使用B对象
- 当A对象释放的时候,一定要对B对象进行一次release,这样才能保住A对象释放了,B对应也会随之释放,避免内存泄漏
- property修饰符
- retain 会自动帮我们生成setter/getter方法内存管理的代码
- assign 不会帮我们生成setter/getter方法内存管理的代码,只会生成普通的setter/getter方法,默认什么都不写就是这个修饰符
- atomic:性能低(单线程)
- noatomic 性能高(多线程) 大部分使用
- 在手动管理内存时,在dealloc中使用self.xxx= nil;即可,相当于调用了set方法,内部进行了release和retain方法
- @class:可以简单的引用一个类,不会导入头文件,可以提升编译效率
- 仅仅高偶素编译器 xxx是一个类,并不包含xxx这个类的所有内容
- 具体使用
- 在.h文件中使用@class引用一个类
- 在.m文件中使用#import包含这个类的.h文件
- 由于import是一个预编译指令,他会将""中的文件拷贝到import所在的位置,并且只要""中的文件发生了变化,就会重新编译一次
- 解决两个类的相互引用,因为@class没有拷贝操作
- 总结:
- 如果在.h中import,假如A拷贝了B,B拷贝了C,如果C修改了,那么B和A都需要重新拷贝,也就是说,只要有简介关系都会重新拷贝
- 如果在.h用@class,在.m中import,如果一个文件发生了变化,只有和这个文件由直接关系的那个文件才会重新拷贝,所以提升了效率
- 如果两个类相互拷贝,如果两个.h都用import,那么A拷贝B,B拷贝A,会造成死循环,如果在.h用class,那么不会做任何拷贝操作,而在.m中用import只会拷贝对应的文件,并不会造成死循环
- 如果A对象要拥有B对象,B对应又要拥有A对象,此时会形成循环retain
- 让其中一个不要做retain操作即可
- autorelease
- 是一种支持引用技术的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子离的所有对象进行一次release操作,注:只会进行一次release,并不保证引用计数一定为0
- 调用autorelease,该对象的引用计数器并不会加一
- 可以避免代码量过大的情况下,需要时时刻刻关注在哪里release的问题
- 当IOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在
- 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
- autorelease只是将release延迟
- 注意事项
- 一定要在自动释放池中调用autorelease,才会将对象放入自动释放池
- 在自动释放池中创建了对象,一定要调用autorelease才会将对象放入自动释放池
- 只要在自动释放池中调用autorelease,就会将对象放入自动释放池,对象的定义可以不在自动释放池
- 不要在自动释放池中使用比较消耗内存的对象,占用内存比较打的对象
- 尽量不要在释放池中使用循环,特别是循环的次数非常多,并且还非常占内存
- 一个程序可以创建N个自动释放池,并且自动释放池还可以嵌套
- 一个alloc/new就对应一个autorelease/release
- 应用场景
- 解决程序员要随时考虑在哪里release的问题
- Foundation框架的类,但凡是通过类工厂方法创建的对象都是autorelease的
- ARC自动引用计数
- ARC是编译时特性,而不是运行时特性,XCode的功能
- 不同于其他语言的垃圾回收,有着本质区别,垃圾回收机制时定时去查看释放对象,而ARC是XCcode自动写代码执行代码
- ARC的优点
- 完全消除了手动管理内存,让程序员专注业务
- 基本上能避免内存泄漏
- 有时性能会更加优化,因为编译器还可以进行一些优化
- ARC的判断原则(忘记引用计数的概念)
- 只要还有一个强指针变量指向对象,对象就会保持在内存中
- 强指针 ————strong
- 默认情况下所有指针都是强指针
- 弱指针 __weak
- 在开发中,不要使用一个弱指针保存一个刚刚创建的对象,因为会因为ARC马上被释放
- 单个对象的内存管理 p=nil;
- MRC:
- A对象向拥有B对象,需要对B进行一次retain
- A对象不用B对象了,需要对B进行一次release
- property的时候进行retain,delloc的时候进行release
- ARC:
- A对象向拥有B对象,那么久需要用一个强指针指向B对象
- A对象不想拥有B对象了,A对象无需做任何操作
- 在ARC中定义变量不使用retain,assign,而是使用strong
- ARC中也存在循环引用
- 可以一个变量用strong,一个变量用weak,不要用assign(可以使用,但是assign是专保存基本数据类型的)
- MRC项目转ARC项目
- 点击Xcode 旁边的Edit,点Convert 选中转ARC,相关过大可能失败