任何继承了NSObject的对象,都需要内存管理,
但是对基本数据类型无效(不需要释放)
原理:
- 每个对象内部都保存了一个与之相关联的整数,称为引用计数器
- 当使用alloc、new或者copy创建一个对象时,对象的引用计数器被设置为1
- 给对象发送一条retain消息,可以使引用计数器值+1
- 给对象发送一条release消息,可以使引用计数器值-1
- 当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收,OC也会自动向对象发送一条dealloc消息。一般会重写dealloc方法,在这里释放相关资源。一定不要直接调用dealloc方法。
- 可以给对象发送retainCount消息获得当前的引用计数器值
向对象发送某个消息,相当于调用某个方法。
内存管理原则:
- 谁创建,谁释放(“谁污染,谁治理”)。如果通过alloc、new或(mutable)copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
- 一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
- 谁retain,谁release。只要你调用了retain,无论这个对象谁如何生成的,你都要调用release
如果在某个对象方法中retain了另一个对象,需要在这个对象释放的时候同时释放retain的那个对象。
如果想要retain对象新的成员变量,先释放掉旧的成员变量;在释放掉旧的成员变量前要先判断新传递进来的成员变量与原来的成员变量是否相同,如果不同则释放掉旧的成员变量,如果相同则不release旧的成员变量,也不进行retain。
@property内存管理新特性:
@property一般帮我们自动生成的setter方法是很简单的方法,不能实现内存管理,例如:
-(void)setBook:(Book*) book{
_book=book;
}
所以我们一般需要自己去实现book的setter方法,例如:
-(void)setBook:(Book *)book{
if(_book!=book){
[_book release];
_book=[book retain];
}
}
假如我们在某个类里要使用很多个@property 语句去使用类的属性的时候,如果还要自己在.m文件中去写setter方法会很麻烦,所以我们可以这么写:
@property (retain)Book* book;
@property (retain)Card* card;
这里的retain代表:release旧的值,retain新的值
这样相当于我们在.m文件中实现了带有内存管理的setter方法(即上面的代码)
@property的其他参数:默认是(readwrite)属性,同时生成get和set方法
@property(assign)int age;这句跟@property int age等价,@property默认就是assign参数的
@property(readonly)int age;这句表示age是一个只读变量,表示只生成get方法
@property
格式:@property(参数1,参数2) 类型 名字;
参数可有可无,比如:
@property int age;
@property (nonatomic,retain)UIButton* btn;
参数主要分为3类:
读写属性:readwrite/readonly
setter处理:assign/retain/copy
原子性:atomic/nonatomic
@property属性默认为atomic,提供多线程安全
- 在多线程环境下,原子操作是必要的,否则就有可能引起错误的结果
- 加了atomic,setter/getter是一个原子操作。如果有多个线程同时调用setter的话,不会出现某一个线程执行setter全部语句之前,另一个线程开始执行setter的情况,相当于函数头尾加了锁一样
nonatomic代表方法不考虑线程安全问题:
- nonatomic表示禁止多线程,变量保护,提高性能
- atomic是OC使用的一种线程保护技术,防止在写入未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iphone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择
- 如果不需要多线程支持的话,用nonatomic就够了,另外由于不涉及锁操作,所以它执行相对快点
@property的setter处理:
assign:默认类型,setter方法直接赋值,而不进行retain操作
retain:setter方法release旧值,再retain新值
copy:setter方法release旧值,再copy新值(暂时先这么理解,后续会有关于copy语法的新解释
@property的读写属性:
readwrite:产生setter和getter
readonly:只产生简单的getter,没有setter
@property的其他用法:
例如:@property(nonatomic,getter=isRich)BOOL rich;
//getter是用来指定getter方法的方法名,同理setter也可以这样用
自动释放池(autorelease pool)
- 自动释放池是OC里面的之中内存自动回收机制,一般可以将一些(临时)变量添加到自动释放池中,统一回收释放
- 当自动释放池销毁时,池里面的所有对象都会调用一次release方法
- OC对象只需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池)
- autorelease实际上只是把对release的调用延迟了(相当于延迟回收对象),对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用renlease
//autoreleasepool代表创建一个自动释放池
@autoreleasepool{
//使用自动释放池:
//之前:
Book* book =[[Book alloc] init];
[student setBook:book];
[book release];
//现在:
Book *book =[[[Book alloc] init] autorelease];
[student setBook:book];
//不用调用[book release]
}
静态方法不需要自己释放内存,我们在开发中会经常用到静态方法,因为我们在实现静态方法时,会把变量放到autorelease中这样就不用我们自己去释放内存了
另外:静态方法不能访问对象的成员变量
autorelease pool注意:
- 在ARC下,不能使用[[NSAutoreleasePool alloc] init],而应当使用@autoreleasepool
- 不要把大量循环操作放到同一个NSAutoreleasePool之间,这样会造成内存峰值的上升
- 尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
- sdk中一般利用静态方法创建并返回的对象都是已经autorelease的,不需要再进行release操作,如[NSNumber numberWithInt:10];返回的对象是不需要再release的。但是通过[[NSNumber alloc] initWithInt:10]创建的对象需要release