Objective-C对象的生命期取决于其引用的计数。在Objective-C的引用计数构架中,有一种特征叫做“自动释放池块”(autorelease pool)。释放队形有两个方式:以后总是调用release方式,时期保留计数立即递减:另一种是调用autorelease方法,将其加入”自动释放池“时,系统会将其中多个对象发送release消息。
自动释放池的原理.
存入到自动释放池中的对象,在自动释放池被销毁的时候.会自动调用存储在该自动释放池中的所有对象的release方法.
可以解决的问题:
将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.
因为池子销毁的时候 就会自动的调用池中所有的对象的relase。
创建自动释放池的语法运用如下:
@autoreleasepool {
//.......
}
如果在没有创建自动释放池的情况下给对象发送autorelease消息,那么控制台就会输出这样一条消息:
Objective 0xabcd0123 of class __NSCFString autorelease
with no pool in place - just leaking - break on objc
autoreleaseNoPool() to doing
然而,一般情况下无需担心自动释放池的创建问题。Mac OS X 与iOS应用程序分别运用于Cocoa及Cocoa Touch环境中个。系统和会自动创建一些路线,比如说主线或者”大中枢派发“(Grand Central Dispatch,GCD)机制中的线程,这些线程默认都是自动释放,每次执行”事件循环“时,就会进将其清空。因此,不需要自己来创建“自动释放吃”同尘个只有一个地方需要创建自动释放池,那就在main函数里。我们用自动释放池来包裹应用程序的主入口。比方说。iOS程序的面函数经常这样写:
int main(int argh, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argh,argv,nil,@"EOCAppDelegate");
}
}
从技术角度来看,不是非得有个“自动释放池”才行。应为块的末尾敲好就是应用程序的终结处,二此时操作系统会把程序所占的全部内存全都释放掉,虽然如此,但是如果不写这个块的话,那么有UIApplicationMain函数所自动释放的那些对象,就没有自动释放池可以容纳了,于是西戎会发出警告信息来表明这一情况。所以说,这个吃可以理解成最外围捕捉自动释放对象所用的池。
下面这段代码中的话括号定义了自动化四方吃的范围。自动释放池于左括号处创建,并于对应右括号处清空。位于自动释放池中的对象,将在此范围末尾处收到release的消息。自动释放吃可以嵌套。系统在自动该释放对象的时候,会把它放到最内层的池里。比方说:
@autoreleasepool {
NSString *string = [NSString stringWithFormat:@"1 - %i",1];
@autoreleasepool {
NSNumber *number = [NSNumber numberWithInt:1];
}
}
使用注意
只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池之中.
如果只是将对象的创建代码写在自动释放之中,而没有调用对象的autorelease方法.是不会将这个对象存储到这个自动释放池之中的.
对象的创建可以在自动释放池的外面,在自动释放池之中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池之中.
如果对象的autorelease方法的调用放在自动释放池的外面,是无法将其存储的这个自动释放池之中的.
autorelease 的调用只有放在自动释放池之中 才可以讲其存储到自动释放池之中, 对象的创建可以在外面
当自动释放池结束的时候.仅仅是对存储在自动释放池中的对象发送1条release消息 而不是销毁对象.
如果在自动释放池中,调用同1个对象的autorelease方法多次.就会将对象存储多次到自动释放池之中.
在自动释放池结束的时候.会为对象发送多条release消息.
所以,1个自动释放池之中,只autorelease1次,只将这个对象放1次, 否则就会出现野指针错误.
如果在自动释放池中,调用了存储到自动释放中的对象的release方法.
在自动释放池结束的时候,还会再调用对象的release方法.
这个时候就有有可能会造成野指针操作.
将对象存储到自动释放池,并不会使对象的引用计数器+1 所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了.
自动释放池可以嵌套.
调用对象的autorelease方法,会讲对象加入到当前自动释放池之中
只有在当前自动释放池结束的时候才会像对象发送release消息.
autorelease的应用场景.
创建对象,将对象存储到自动释放池之中. 就不需要再去手动的realse。
我们一般情况下,会为我们的类写1个类方法,用来让外界调用类方法来快速的得到1个对象.
规范:使用类方法得到的对象,要求这个对象就已经被autorelease过了.
提供1个类方法来快速的得到1个对象.
规范
这个类方法以类名开头. 如果没有参数就直接是类名 如果有参数就是 类名WithXX:
使用类方法得到的对象,要求这个对象就已经被autorelease过了.
- (instancetype)person
{
return [[[self alloc] init] autorelease];
}
以下是我整理的需要注意的事项:
(1)在自动释放池@autoreleasepool{}中alloc一个对象后,仍然需要用[p1 autorelease];只是这个语句和[p1 release];不同,后者表示把p1的retainCount-1,而前者仅仅表示把p1放到自动释放池中返回一个self,自动释放池结束销毁时,统一对里面的对象引用计数retainCount-1。
(2)@autoreleasepool{}可以随意创建,也可以嵌套使用。
(3)不管这个对象是在自动释放池内还是外创建的,只要在自动释放池内写一个[p1 autorelease];p1就会被放到自动释放池中。注意autorelease是一个方法,且只有在自动释放池中使用才有效。
(4)如果把一个对象重复加到自动释放池如[p1 autorelease];[p1 autorelease];,那么会出错。原因是:加载几次,届时自动释放池就会用[p1 release];释放几次,但是由于这两个加载的对象其实是一个对象同样地址,所以第一次自动释放正确,第二次自动释放时发现已经被释放了,所以p1就变成了野指针。
(5)以下是自动释放池嵌套的使用规则和注意点。
[objc] view plain copy 在CODE上查看代码片派生到我的代码片
import <Foundation/Foundation.h>
import "Person.h"
int main(int argc, const charchar * argv[]) {
Person *p1=[[Person alloc]init];
@autoreleasepool {
@autoreleasepool {
[p1 autorelease];
}//在执行到此处时,p1被自动释放
}
//以下代码有错误
@autoreleasepool {
[p1 autorelease];//此时p1被加入进来
@autoreleasepool {
[p1 autorelease];//被重复加载进来,但仍然同一个
}//此处,p1被自动释放了,所以第一次加进来的那个也被释放了,因为是同一个对象
}//所以此处在调用[p1 release];时就出现报错:野指针
return 0;
}
(6)@autoreleasepool的应用:如果需要在方法中创建对象,并把这个对象作为返回值,那么可以在这个方法中使用[*** autorelease];把它加入到自动释放池中,否则,直接用[*** release];来匹配alloc的话,在该方法中就已经把这个对象alloc和release了一遍相当于释放了,那么所谓的返回对象返回的时一个野指针(没有指向任何对象)。当然,调用这个方法的代码页需要写在自动释放池作用域内才生效。
(7)接上面。返回对象的那个方法中,创建对象不建议直接用类名,而是用self,否则如果存在子类调用会崩溃。如Car *car1=[[self alloc]init];
(8)其实诸如NSString *str1=[NSString stringWithFormat:@"%@",@"hello"];也是调用了一个方法,并且返回了一个字符串对象。比照(6)和(7)我们得知这个stringWithFormat应该也是顺便返回了一个autorelease。
(9)在ARC机制中,我们用@property声明的成员变量,建议用strong代替之前手动管理内存时的retain,虽然后者仍然可以使用。因为我们在ARC中内存管理就是看是否有强指针指向对象,如有就不回收,如没有就回收。所以强指针是strong,相反是weak。而基本数据类型我们还是习惯用assign。
(10)虽然Xcode提供了非ARC转换成ARC的,很少有把整个非ARC转换成ARC的。如果我们导入第三方库时,需要非ARC和ARC共存,即我们系统默认是ARC,我们需要让系统不要去管这个非ARC的第三方库,如下设置:双击响应的.m文件,输入-fno-objc-arc回车即可。
(11)顺便,当出现两个类循环引用的话(也就是A要包含B,B要包含A,即A对象要作为B的变量,B对象要作为A的变量),只需要把一方的strong改成weak,并且在响应的.h文件中把#import ".h"改成Class ***。如果因为改成Class ***而无法使用那个类的方法的话,只需要在它的.m文件中#import“.h”文件即可,这个因为不是在.h文件中导入所以不冲突。
这就是我整理的关于Autorelease的使用以及“自动释放池块”降低内存峰值的使用方法