有时候我们需要延迟一个对象
的引用计数减一操作,比如:
+(NSArray*)array
{
return[[NSArrayalloc]init]
autorelease];
}
由于方法名并不以alloc,new,
copy,mutableCopy开头,并且
方法内部使用了alloc,需要对
因此产生的引用计数负责。
不过如果直接调用release,将
会返回野指针,所以我们需要
autorelease机制来延迟释放。
我们需要先创建一个
autoreleasepool,才能有效地
实现autorelease机制,否则会
导致内存泄露。
当向一个对象obj发送
autorelease消息时,会发生如
下过程:
获取当前autoreleasepool,即
离事件发生处最近的(inner-
most)自动释放池,
NSAutoreleasePool实例。
NSAutoreleasePool实例将obj
添加到内部维护的数组中,记
录起来。
当发送drain消息给自动释放池
时,它会遍历内部维护的数组
,向每个元素发送一个release
消息。
除了我们显式创建的
NSAutoreleasePool实例,系
统会默认为我们在主线程创建
一个:
@autoreleasepool{
return
UIApplicationMain(argc,argv,
nil,
NSStringFromClass([AppDele
gateclass]));
}
从可见代码来看,这个自动释
放池建立在main函数中,对于
我们编码过程中创建的自动释
放对象有什么帮助呢?
等到结束UIApplicationMain才
释放,跟不释放基本没差别,
因为程序运行即将结束。
所以,如果事实真相如此,那
么是不合理的。
苹果官方文档有这么一段话:
TheApplicationKitcreatesan
autoreleasepoolonthemain
threadatthebeginningof
everycycleoftheeventloop,
anddrainsitattheend,
therebyreleasingany
autoreleasedobjects
generatedwhileprocessingan
event.Ifyouusethe
ApplicationKit,youtherefore
typicallydon’thavetocreate
yourownpools.Ifyour
applicationcreatesalotof
temporaryautoreleased
objectswithintheeventloop,
however,itmaybebeneficial
tocreate“local”autorelease
poolstohelptominimizethe
peakmemoryfootprint.
可见,在主线程中每个event
loop开始的时候会创建一个
autoreleasepool,在循环结束
时清空自动释放池。
当然,如果在某个局部地方创
建很多临时对象,最好也显式
创建autoreleasepool降低内存
峰值。
用代码来求证:
-(void)touchesBegan:(NSSet
*)toucheswithEvent:(UIEvent
*)event
{
TestObject*testObject=
[[[TestObjectalloc]init]
autorelease];
}
-(void)touchesEnded:(NSSet
*)toucheswithEvent:(UIEvent
*)event
{
NSLog(@"TouchesEnded
");
[NSAutoreleasePool
showPools];
}
通过在TestObject的release方
法中输出日志信息,可以看到
testObject是在touchesEnded
事件前被释放的,即event
loop结束前。
这是对主线程而言,那么对其
它线程呢?
下面又有一段说明:
Eachthread(includingthe
mainthread)maintainsitsown
stack
ofNSAutoreleasePoolobjects
(see“Threads”).Asnewpools
arecreated,theygetaddedto
thetopofthestack.When
poolsaredeallocated,they
areremovedfromthestack.
Autoreleasedobjectsare
placedintothetop
autoreleasepoolforthe
currentthread.Whenathread
terminates,itautomatically
drainsalloftheautorelease
poolsassociatedwithitself.
从上面这段话可以得知每个线
程都维护着与自己对应的
NSAutoreleasePool对象,将
其放在线程栈的栈顶。当线程
结束时,会清空自动释放池。
同样地可以用代码来说明:
[NSThread
detachNewThreadSelector:@
selector(onNewThread:)
toTarget:selfwithObject:nil];
-(void)onNewThread:(id)info
{
TestObject*testObject=
[[[TestObjectalloc]init]
autorelease];
}
通过输出结果也可以得知线程
结束时,testObject得到释放。
不过,紧接着的一段说明说明
了些例外情况:
IfyouaremakingCocoacalls
outsideoftheApplicationKit’s
mainthread—forexampleif
youcreateaFoundation-only
applicationorifyoudetacha
thread—youneedtocreate
yourownautoreleasepool.
不过当我detach了多个线程出
来,发现每个线程仍然有维护
autoreleasepool。是不是上面
这段话我哪里没理解对?幸好
在没有autoreleasepool存在时
,我们向对象发送autorelease
消息会有警告输出。
最后,探讨下NSRunLoop和
autorelease的关系。
当我们分离了个线程,并且让
runloop跑起来,我们可以发现
与主线程类似:每个runloop循
环结束时会drain下autorelease
pool。
通过观察runloop状态可以验证
:
#pragmamark-RunLoop
Observer
-(void)onNewThread:(id)info
{
NSRunLoop*runloop=
[NSRunLoop
currentRunLoop];
if(!runloop){
return;
}
NSTimer*timer=[NSTimer
scheduledTimerWithTimeInter
val:5.0ftarget:self
selector:@selector(onTimerFir
ed:)userInfo:nilrepeats:NO];
[runloopaddTimer:timer
forMode:NSRunLoopCommon
Modes];
CFRunLoopObserverConte
xtcontext={
0,
self,
NULL,
NULL,
NULL
};
CFRunLoopObserverRef
observerRef=
CFRunLoopObserverCreate(k
CFAllocatorDefault,
kCFRunLoopAllActivities,
YES,0,
&runloopObserverCallback,
&context);
CFRunLoopAddObserver([r
unloopgetCFRunLoop],
observerRef,
kCFRunLoopCommonModes);
[runlooprun];
CFRunLoopRemoveObserv
er([runloopgetCFRunLoop],
observerRef,
kCFRunLoopCommonModes);
CFRelease(observerRef);
}
staticvoid
runloopObserverCallback(CF
RunLoopObserverRef
observer,CFRunLoopActivity
activity,void*info)
{
CFRunLoopActivity
currentActivity=activity;
switch(currentActivity){
casekCFRunLoopEntry:
NSLog(@"kCFRunLoo
pEntry
");
break;
casekCFRunLoopBefore
Timers:
NSLog(@"kCFRunLoo
pBeforeTimers
");
break;
casekCFRunLoopBefore
Sources:
NSLog(@"kCFRunLoo
pBeforeSources
");
break;
casekCFRunLoopBefore
Waiting:
NSLog(@"kCFRunLoo
pBeforeWaiting
");
break;
casekCFRunLoopAfterW
aiting:
NSLog(@"kCFRunLoo
pAfterWaiting
");
break;
casekCFRunLoopExit:
NSLog(@"kCFRunLoo
pExit
");
break;
default:
NSLog(@"Activitynot
recognized!
");
break;
}
}
----------------------------------------
----------
评论0
最新资源