原文学习记录来自:文顶顶博客园
一、内存管理基本原理
1、管理范围:
任何继承NSObject的对象 (对其他的基本数据类型无效)
本质原因:
- 对象和其他数据类型在系统中的存储空间不一样(其他局部变量主要存储于栈中,而对象存储于堆中)
- 当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在与内存中,造成内存泄漏。
2、引用计数器
(1)是判断对象要不要回收的唯一依据-----引用计数器是否为0(0则销毁,反之则对象不销毁)
(2)引用计数器 通过 retain copy alloc new等方法 使对象的引用计数器+1
(3)每个对象都有自己的引用计数器(对象被创建时,引用计数器值默认为1)
(4)早每个OC对象内部,都专门有4 个字符的存储空间来存储引用计数器
3、对象的销毁
(1)当对象的引用计数器为0时,对象会被销毁,其占用的内存会被系统回收
(2)对象被销毁时,系统会自动想对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源。
一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)
(3)一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)。
4、相关概念和使用注意
(1) 野指针错误:访问了一块坏的内存(已经被回收,不可用的内存)
(2)僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(解决:打开僵尸对象检测)
(3)空指针:没有指向任何东西的指针(存储的东西是null ,nil,0),给空指针发送消息不会报错
注:不能使用[p retain]让僵尸对象起死复生
二、内存管理原则
谁创建(retain /copy / alloc / new) 谁释放(release / autrelease)
三、内存管理代码规范
(一)Set方法的代码规范
1、基本类型,直接复制
-(void)setAge:(int )age{
_age = age;
}
2、对象类型
-(void)setCar : (Car *)car {
if(_car != car) // 判断是不是新传进来的对象
{
[_car release]; // 对旧对象做一次release 若没有旧对象则没影响
_car = [car retain]; // 对新对象做一次retain
}
}
(二)Deallco方法的代码规范
(1)一定要[super dealloc],而且要放到最后
(2)对self(当前)所拥有的其他对象做一次release操作
-(void)dealloc {
[_car release];
[super dealloc];
}
四、内存管理中的循环引用问题以及解决
例:每个人有一张身份证,每张身份证对应一个人,不能使用#import方式相互包含,这就形成了循环引用。
1、关键字 @class 类名;------- 来解决循环引用问题,提高性能。
(1)@class关键字作用
@class 仅仅告诉编译器,在进行编译的时候把后面的名字作为一个类来处理
@class 作用----- 声明一个类,告诉编辑器某个名称是一个类
@class 开发中引用一个类的规范
(2)使用方法
在 .h文件中使用@class来声明类 在.m文件中真正使用到的时候,使用#import来包含类中的所有东西
2、两端循环引用的解决方法
一端使用retain,一端使用assign(使用assign的dealloc中也不再release)
五、Autorelease使用
1、基本用法
- 会将对象放到一个自动释放迟中
- 当自动释放池被销毁时,会对池子里的所有对象做一次release
- 会返回对象本身
- 调用完auto release方法后,对象的计数器不受影响(销毁时影响)
2、好处
- 不需要再关心对象的释放时间
- 不需要再关心什么时候调用release
3、使用注意
- 占用内存较大的对象,不要随便使用autorelease,应该使用release来精确控制
- 占用内存较小的对象使用autorelease,没有太大的影响
4、错误写法
- 连续调用多次Autorelease,释放池销毁时执行两次release
- Alloc之后调用了Autorelease,之后又调用了release
5、自动释放池
- 在iOS运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的
- 当一个对象调用autorelease时,会将这个对象放到栈顶的释放池中
6、自动释放池的创建方式
- iOS5.0以前的创建方式
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
......
[pool release]; // [pool drain];用于Mac
ios5以后
autorelease
{ // 开始代表创建自动释放池
。。。。。。
}// 结束表示销毁自动释放池
7、Autorelease注意
- 系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate date];
- 开发中经常会写一些类方法来快速创建一个autorelease 对象,创建对象时不要直接使用类名,而是使用self
五、ARC内存管理机制
(一)ARC的判断标准
只要没有强指针指向对象,对象就会被释放。
(二)指针分类
-
强指针:默认的情况下,所有的指针都是强指针,关键字是strong
-
弱指针:_weak关键字修饰的指针
// 声明一个弱指针如下
__weak Person *p;
ARC中,只要弱指针指向的对象不在了,就直接把弱指针做清空操作。
// 不合理,对象一创建出来就被释放掉,对象释放掉后,ARC把指针自动清零
__weak Person *p = [[Person alloc] init];
- ARC中的property处不在使用retain,而是使用strong,在dealloc中不需要再[super dealloc];
// 意味着生成的成员变量_dog是一个强指针,相当于以前的retain
@property (nonatomic , strong) Dog *dog;
- 如果指针是弱指针,则换成weak,不需要加__;
(三)ARC的特点总结
- 不允许调用release、retain、retainCount(获取当前对象的引用计数器数)
- 允许重写dealloc,但是不允许调用[super dealloc]
- @property的参数
- Strong: 相当于原来的retain(适用于OC对象),成员变量是强指针
- Weak: 相当于原来的assign(适用于OC对象),成员变量是弱指针
- assign: 适用于非OC对象类型(基础类型)
(四)补充
- 程序兼容ARC 和 非ARC 部分
- 转变为非ARC -fno-objc-arc
- 转变为ARC -f-objc-ar
- ARC循环引用问题:一端使用Strong,另一端使用assign
- 提示:字符串是特殊的对象,但不需要使用release手动释放,这种字符串对象默认就是autorelease的,不用额外的去管理内存
目录