-----------android培训、java培训、java学习型技术博客、期待与您交流!-----------
OC内存管理的原理
一、OC内存管理的原理
范围:所有继承至 NSObject 的对象的内存管理,为了防止内存泄露
1、对象的所有权及引用计数
2、对象所有权:任何对象都可能拥有一个或者多个所有者,只要一个对象至少还拥有一个所有者,他就会继续存在
3、所有权策略:任何自己创建的对象都归自己所有,使用alloc new 或者copy的方法创建对象 , 都会有一个所有者,就是他自己,可以使用retain获取一个对象的所有权
4、对象的引用计数器:用一个整数表示对象被引用的次数,对象创建时默认的引用计数是1,当引用计数变为0时,对象被销毁
5、引用计数器是判断对象要不要回收的依据就是计数器是否为0,若不为0则存在。 存在例外是:对象值为nil时,引用计数器为0时,但不回收空间
6、retain消息:使计数器+1,该方法返回对象本身
7、release消息:使计数器-1,并不代表释放对象
8、retainCount消息:存储引用计数器,获得当前引用计数器值 用%ld %tu
二、对象销毁
1、当一个对象的引用计数器为0时,那么它将被销毁,占用的内存被系统回收
2、当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,释放相关的资源,delloc就像对象的”临终遗言“
3、一旦重写dealloc方法就必须调用 [ super dealloc ],并且放在代码块最后调用,不能直接调用dealloc方法
4、一旦对象被回收了,那么他所占用的存储空间就不再可用,坚持使用会导致程序崩溃,造成野指针错误
注意:
1、如果对象的计数器不为0,那么整个程序运行过程中,他所占用的内存就不可能被回收
2、任何一个对象,刚生下来的时候,引用计数器为1 ,当用alloc new 或者copy 创建一个对象时,对象的引用计数器默认就是1
三、OC中内存管理分类
提供三种内存管理方式
ARC、MRC 和垃圾回收(ios不支持垃圾回收)
四、内存管理原则
1、原则
只要有人使用某个对象,对象就不会被回收
如果想使用这个对象,就要让对象的引用计数+1
当不使用这个对象是,应该让对象引用计数器-1
2、谁创建、谁release
(1)如果通过alloc new,copy 创建一个对象,就必须用release 或者autorelease方法,否则会内存泄露
(2)不创建就不负责
3、谁retain,谁release
(1)只要调用了retain,无论对象如何生成的,都要用release
4、内存管理的内容
(1)野指针(僵尸对象):1、定义的指针变量没有初始化 2、指向的空间已经被释放
(2)内存泄露:栈区指针释放,而堆区的空间没有释放,堆区的没有释放的空间发生内存泄漏
五、单个对象内存管理(野指针)
野指针访问:访问了一块(已经被回收的,不可用的)坏的内存
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再使用
空指针:没有指向任何东西的指针,给空指针发送消息不会报错
nil :给一个对象赋值
Nil:给一个类赋值
NULL:通用指针
NSNull:[NSNull null]是一个对象,他用在不能使用nil的场合
注意:
1、不能使用[p retain]让僵尸对象复活,已经死了不能复生
2、单个对象中避免使用僵尸对象的方法是:对象释放后,给对象赋值为nil
3 、单个对象的内存泄露情况之一是创建完成使用后,没有release
4、单个对象的内存泄露情况之二是使用retain后没有release
5、单个对象的内存泄露情况之三是nil的不当使用,对象被创建之后为release
例如:Dog *d = [ [ Dog alloc ] init ];
d = nil; //将对象赋值为nil(空)
[d eat]; //对象调用方法时实质上是nil调用
[d release ]; //对象release时实质上是nil release
6、单个对象的内存泄露情况之四是在方法中对传入的对象内部 retain 后,没有 release
六、多对象的内存管理
1、野指针问题的避免方法是:在方法内部进行retain
2、多对象中内存泄露解决方法是:在 dealloc方法中让别的对象release,也可以是使用 self.对象名 = nil
七、set方法中的内存管理
set方法的写法
如果在一个类中,有其他类的对象(关联关系例如凤姐开车去拉萨的例子)当有多个对象并且传给set方法的两个对象不一样时,前一个被传入的对象就会发生泄漏,因此需要下面的set写法,先判断传入的对象是不是新的,如果是新的就先release旧对象,再retain新对象,如果不是新的就直接将对象retain 一次
-(void)setCar:(Car *)car
{
//1、先判断是不是新传进来的对象
if( car != _car)
{
//2、对旧对象做一次release
[ _car release ];
//3、对新对象做一次retain
_car = [ car retain ];
}
}
八、对象作为另一个类的实例变量的固定写法
-(void)setDog:(Dog *)dog{
//1、判断对象否是原对象
if(_dog != dog) {
//release旧值
[ _dog release];
// retain新值,并且赋值给实例变量
_dog = [ dog retain ];
}
}
九、autorelease的基本使用
1、自动释放池
在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的
当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中
2、自动释放池的创建
@autoreleasepool
{//开始代表创建自动释放池
.........
}//结束代表销毁自动释放池
3、autorelease基本用法
(1)创建自动释放池
@autoreleasepool
{//开始代表创建自动释放池
.........
}
(2)加入自动释放池,加入之后引用计数器不会发生变化
@autoreleasepool
{//开始代表创建自动释放池
[ 对象 autorelease ];
}// 此处自动释放池结束时会给池中的对象发送一条 release 消息,此时对象被释放
十、autorelease 的使用场景
1、经常用来在类方法中快速创建一个对象
十一、autorelease的注意事项
1、在MRC下,对象即使放到自动释放池的代码中,需要加入自动释放池中 ([对象 autorelease])才能释放;
2、如果对象没有放在任何一个自动释放池中,在自动释放池代码外面调用自动释放( [对象 autorelease];)对象也不会被释放
3、不管对象是在什么地方创建,只需在 自动释放池代码块中调用autorelease就可以把对象加入到自动释放池中释放
4、嵌套使用,autorelease只需加入一次,不能连续多次使用,和release不同
5、自动释放池不能放占用内存太大的对象
十二、autorelease错误用法
1、连续多次加入到autorelease中
2、alloc之后调用了autorelease,之后又调用了release