iOS开发--“自动释放池块”降低内存峰值

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的使用以及“自动释放池块”降低内存峰值的使用方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 2,008评论 1 16
  • 29.理解引用计数 Objective-C语言使用引用计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数...
    Code_Ninja阅读 1,532评论 1 3
  • iOS内存管理 概述 什么是内存管理 应用程序内存管理是在程序运行时分配内存(比如创建一个对象,会增加内存占用)与...
    蚊香酱阅读 5,760评论 8 119
  • 我生君晚生 君成我心老 恨熟不同时 感错才知晓 君来我已去 我来君已往 恨遇不逢时 日日与君好 君来之吾悦 君去之...
    琪人书兰阅读 244评论 0 0
  • 亲爱的小苹果(小屁股): 我们的相处就如我对你的称呼一样,大家都应该想象得出我们是如此多姿的相处。 这一年中你已增...
    夕阳老去西风渐紧阅读 294评论 2 1