oc 内存管理
基本原理什么是内存管理
移动设备的内存极其有限,每个app所能占用的内存是有限制的
当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等
管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象
每个OC对象内部专门有4个字节的存储空间来存储引用计数器
引用计数器的作用
当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出
引用计数器的操作
给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
给对象发送一条release消息,可以使引用计数器值-1
可以给对象发送retainCount消息获得当前的引用计数器值
对象的销毁
当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
不要直接调用dealloc方法
一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
1.方法的基本使用
1> retain : 计数器+1,会返回对象本身
2> release : 计数器-1,没有返回值
3> retainCount : 获取当前的计数器
4> dealloc
* 当一个对象要被回收的时候, 就会调用
* 一定要调用[super dealloc],这句调用要放在最后面
2.概念
1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
看下面的例子:
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *p = [[Person alloc] init]; //用alloc创建一个新对象p, p的引用计数器默认是1
NSUInteger c = [p retainCount]; //retainCount用来获取对象当前计数器的值
NSLog(@"计数器:%ld", c); //输出的值为1;
[p retain]; // [retain]计数器加1:方法返回的是对象本身,现在是2
[p release]; // [release]计数器减1: 方法返回的是对象本身,现在是1
[p release]; // [release]计数器减1:现在是0; p指向一个死了的对象, p现在就是野指针, 指向了一个僵尸对象,
[p retain]; // 当再[p retain]时会报后面的错误,-[Person setAge:]: message sent to deallocated instance 0x100109a10
[p setAge:10]; // 给已经释放的对象发送了一条-setAge:消息:同样报上面的错误
p = nil; // 指针p变成空指针
// EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存
// 野指针错误
[p release]; // OC不存在空指针错误,给空指针发送消息,不报错
return 0;
}
只要还有人在用某个对象,那么这个对象就不会被回收
只要你想用这个对象,就让对象的计数器+1
当你不再使用这个对象时,就让对象的计数器-1
谁创建,谁release
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
换句话说,不是你创建的,就不用你去[auto]release
谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release
看下面的例子:
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Book.h"
int main()
{
Book *b = [[Book alloc] init]; //用alloc创建一本书,书的计数器现在为1;
Person *p1 = [[Person alloc] init]; //用alloc创建一个人,人得计数器现在为1
[p1 setBook:b]; //人想用这本是, 就给书的计数器加一 [b retain] p1现在计数器是1,b现在的计数器是2
[p1 release]; //人被释放了, p1的计数器现在变被0; 当p1挂掉以后,就去看他的遗言dealloc,再把它[retain]的书给释放掉[dealloc] b现在的计数器为1
p1 = nil; //p1空指针
[b release]; //[b release] b现在的计数器是 0;
b = nil; //b空指针
return 0;
}
执行结果
Person对象被回收
Book对象被回收
---------------------------------------------------------------------------
#import "Book.h"
@implementation Book
- (void)setPrice:(int)price
{
_price = price;
}
- (int)price
{
return _price;
}
- (void)dealloc
{
NSLog(@"Book对象被回收");
[super dealloc];
}
@end
-----------------------------------------------------------------------------
#import "Person"
@implementation Person
- (void)setBook:(Book *)book
{
_book = [book retain];
}
- (Book *)book
{
return _book;
}
- (void)dealloc //当人得计数为0时 自动执行dealloc
{
[_book release]; //因为人[b retain]过, 所以当p1挂掉的时候, 就把以前的b释放掉 [b dealloc]
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
-------------------------------------------------------------------------------
总结
有始有终,有加就有减
曾经让对象的计数器+1,就必须在最后让对象计数器-1