autorelease && autorelease pool block
autorelease pool block提供了一个允许我们延时deallocated
对象的机制
autorelease
NSAutoReleasePool
对象的生存周期相当于C语言变量的作用域。对于所有调用过autorelease
的实例方法,会在废弃NSAutorelease对象时,调用release方法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain]; //调用pool drain方法相当于调用[obj release]
在Cocoa框架也有很多可以返回autorelease对象的方法
//返回autorelease对象
id array = [NSMutableArray Array];
//相当与
id array = [[[NSMutableArray alloc] init] autorelease];
autorelease 原理
在NSAutoreleasePool维持了一个数组来保存autorelase的对象,[obj autorelease]方法实际上是调用了NSAutoreleasePool
的[NSAutoreleasePool addObject:obj]
方法,该方法会找到当前的pool并add到数组中,当调用[pool drain]
时会废弃当前pool对象,并释放内部数组,因此内部所有对象都会被调用release方法
可以使用
[NSAutoreleasePool showPools];
这个非公开方法查看现在的pool中对象状况,查看结果类似下面输出,可以用其查看对象是否被自动release
objc[41725]: ##############
objc[41725]: AUTORELEASE POOLS for thread 0x10cd6f5c0
objc[41725]: 2337 releases pending.
objc[41725]: [0x7ff2b4000000] ................ PAGE (full) (cold)
objc[41725]: [0x7ff2b4000038] 0x6000012fd180 __NSArrayI
objc[41725]: [0x7ff2b4000040] 0x6000024aa1c0 __NSSetI
objc[41725]: [0x7ff2b4000048] ################ POOL 0x7ff2b4000048
objc[41725]: [0x7ff2b4000050] 0x6000007a8480 __NSCFString
...
objc[41725]: [0x7ff2b4000058] ################ POOL 0x7ff2b4000058
objc[41725]: [0x7ff2b406ea10] ################ POOL 0x7ff2b406ea10
objc[41725]: [0x7ff2b406ea18] 0x6000005a77c0 NSObject
objc[41725]: ##############
autorelease pool block
在Cocoa框架中,主循环的RunLoop会对NSAutoreleasePool
进行生成、持有和废弃,因此,通常,我们不需要创建自己的自动释放池,但是有些情况下我怕们需要这么做
在autorelease pool block结束时,所有在block内部收到autorelease
消息的对象都会被发送一个release
消息
autorelease pool block可以嵌套:
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
Cocoa中代码都是在自动释放池block中执行,否则会自动释放的对象未被释放造成内存泄漏。(因此,如果在自动释放池block外执行走动式房消息,Cocoa会报错)。AppKit或者UIKit框架都在自动释放池block中处理每个事件循环(详情见runLoop)。因此,通常不需要创建autorelease pool block,但是以下三种情况可能需要使用自己的自动释放池:
- 假如编写不基于UI框架的程序,例如命令行工具
- 假如编写一个创建了很多临时对象的循环
可以再循环内的下一次迭代之前处理这些对象,有助于减少应用程序的最大内存占用量 - 如果你创建了一个辅助线程
当你的线程开始执行时,必须创建自己的自动释放池,否则你的应用程序有可能会内存泄漏
使用本地自动释放池block减少峰值内存占用
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
在上面例子中,for循环每次循环的自动释放block结束时,将自动释放对象(例如:fileContents
)发送release消息
在自动释放池block后,块中的自动释放对象不应该再向其发送消息或者返回。如果想要将其返回,则可以先将其retain
然后在block外将其autorelease
– (id)findMatchingObject:(id)anObject {
id match;
while (match == nil) {
@autoreleasepool {
/* Do a search that creates a lot of temporary objects. */
match = [self expensiveSearchForObject:anObject];
if (match != nil) {
[match retain]; /* Keep match around. */
}
}
}
return [match autorelease]; /* Let match go and return it. */
}
线程和自动释放池block
Cocoa
应用程序的每个线程都会维护自己的自动释放池堆栈。如果编写基于Foundation
的程序或者分离线程,则你必须创建自己的autoreleasePool
如果应用程序或者线程长时间运行,则有可能产生大量自动释放对象,此时就需要使用自动释放池(例如主线程的UIKit和AppKit);否则自动释放对象会累计,造成内存增加。但,如果你的线程没有进行Cocoa调用,则不需要使用自动释放池block
注意:
如果使用POSIX线程API而不是NSThread创建辅助线程,则除非Cocoa处于多线程模式,否则不能使用CocoaCocoa仅在分离其第一个NSThread对象后才进入多线程模式。 要在辅助POSIX线程上使用Cocoa,您的应用程序必须首先分离至少一个NSThread对象,该对象可以立即退出。 您可以使用NSThread类方法isMultiThreaded测试Cocoa是否处于多线程模式。