Objective-C學(xué)習(xí)筆記Cocoa生命周期
Objective-C學(xué)習(xí)筆記Cocoa生命周期是本文要介紹的內(nèi)容,主要是來(lái)學(xué)習(xí)Cocoa中對(duì)象的生命周期,對(duì)象的生命周期包括誕生(通過(guò)alloc或new方法實(shí)現(xiàn))、生存(接收消息和執(zhí)行操作)、交友(借助方法的組合和參數(shù))、釋放(當(dāng)它們的生命結(jié)束時(shí)最終死去)。當(dāng)對(duì)象的生命周期結(jié)束時(shí),它們的原材料(內(nèi)存)將被回收以供新的對(duì)象使用。
1、引用計(jì)數(shù)(reference counting):
每個(gè)對(duì)象有一個(gè)與之相關(guān)聯(lián)的整數(shù),稱(chēng)做它的引用計(jì)數(shù)器或保留計(jì)數(shù)器。當(dāng)某段代碼需要訪問(wèn)一個(gè)對(duì)象時(shí),該代碼將該對(duì)象的保留計(jì)數(shù)器值加1,表示“我要訪問(wèn)該對(duì)象”。當(dāng)這段代碼結(jié)束對(duì)象訪問(wèn)時(shí),將對(duì)象的保留計(jì)數(shù)器值減1,表示它不再訪問(wèn)該對(duì)象。當(dāng)保留計(jì)數(shù)器值為0時(shí),表示不再有代碼訪問(wèn)該對(duì)象了,因此對(duì)象將被銷(xiāo)毀,其占用的內(nèi)存被系統(tǒng)回收以便重用。
當(dāng)使用alloc、new或copy(生成接收對(duì)象的一個(gè)副本)創(chuàng)建一個(gè)對(duì)象時(shí),對(duì)象的保留計(jì)數(shù)器值被設(shè)置成1.要增加對(duì)象的保留計(jì)數(shù)器值,可以給對(duì)象發(fā)送一條retain消息,要減少對(duì)象的保留計(jì)數(shù)器值,可以給對(duì)象發(fā)送一條release消息。
當(dāng)一個(gè)對(duì)象因其保留計(jì)數(shù)器歸0而將被銷(xiāo)毀時(shí),Objective-C自動(dòng)向?qū)ο蟀l(fā)送一條dealloc消息。你可以在自己的對(duì)象中重寫(xiě)dealloc方法。可以通過(guò)這種方式釋放已經(jīng)分配的全部相關(guān)資源。一定不要直接調(diào)用dealloc方法。要獲得保留計(jì)數(shù)器的當(dāng)前值,可以發(fā)送retainCount消息。簽名如下:
- - (id) retain;
- //申請(qǐng)- (void) release;
- //釋放- (unsigned) retainCount;
- //當(dāng)前計(jì)數(shù)器值
- 例如:[[car retain] setTire: tire atIndex:2]; 表示要求car對(duì)象將其保留計(jì)數(shù)器值加1并執(zhí)行setTire操作。Example:
- //
- // Main.m
- // RetainCount1
- // 內(nèi)存管理練習(xí)1
- //
- // Created by Elf Sundae on 10/23/10.
- // Copyright 2010 Elf.Sundae(at)Gmail.com. All rights reserved.
- //
- #import <Cocoa/Cocoa.h>
- @interface RetainTracker : NSObject
- @end15
- @implementation RetainTracker
- - (id) init
- {
- if (self = [super init])
- {
- NSLog(@"init: Retain count of %d.",[self retainCount]);
- }
- return self;25 }
- - (void) dealloc28 {
- NSLog(@"dealloc called.Bye Bye.");
- [super dealloc];31 }
- @end
- int main (int argc, const char * argv[])
- {
- RetainTracker *tracker = [RetainTracker new];
- [tracker retain];
- NSLog(@"%d",[tracker retainCount]);
- [tracker retain];
- NSLog(@"%d",[tracker retainCount]);
- [tracker release];
- NSLog(@"%d",[tracker retainCount]);
- [tracker release];
- NSLog(@"%d",[tracker retainCount]);
- [tracker retain];
- NSLog(@"%d",[tracker retainCount]);
- [tracker release];
- NSLog(@"%d",[tracker retainCount]);
- [tracker release];
- return 0;66 }
輸出:
- init: Retain count of 1.
- dealloc called.Bye Bye.
2、對(duì)象所有權(quán)(object ownership):
如果一個(gè)對(duì)象具有指向其他對(duì)象的實(shí)例變量,則稱(chēng)該對(duì)象擁有這些對(duì)象。這意味著該實(shí)體要負(fù)責(zé)確保對(duì)其擁有的對(duì)象進(jìn)行清理。
當(dāng)多個(gè)實(shí)體擁有某個(gè)特定對(duì)象時(shí),對(duì)象的所有權(quán)關(guān)系就更復(fù)雜了,這也是保留計(jì)數(shù)器值可能大于1的原因。在上面的RetainCount1例子中,main()函數(shù)擁有RetainTracker類(lèi)的對(duì)象,因此main()要負(fù)責(zé)清理該類(lèi)的對(duì)象。
回憶一下在OOP基礎(chǔ)章節(jié)里Car的engine setter方法:
- - (void) setEngine: (Engine *) newEngine;
以及如何在main()函數(shù)中調(diào)用該方法:
- Engine *engine = [Engine new];[car setEngine: engine];
現(xiàn)在哪個(gè)實(shí)體擁有engine對(duì)象?是main()函數(shù)還是Car類(lèi)?
下面是編寫(xiě)setEngine的一種更好的方法:
- - (void) setEngine: (Engine *) newEngine
- {
- [newEngine retain];
- [engine release];
- engine = newEngine;
- } // setEngine
在訪問(wèn)方法中,如果先保留新對(duì)象,然后再釋放舊對(duì)象,這不會(huì)出問(wèn)題。
- In your accessors, if you retain the new object before you release the old object, you’ll be safe.
3、自動(dòng)釋放(Autorelease)
自動(dòng)釋放池(autorelease pool),它是一個(gè)存放實(shí)體的池(集合),這些實(shí)體可能是對(duì)象,能夠被自動(dòng)釋放。
NSObject類(lèi)提供了一個(gè)autorelease方法:
- - (id) autorelease;
該方法預(yù)先設(shè)定了一條在將來(lái)某個(gè)時(shí)間發(fā)送的release消息,其返回值是接收消息的對(duì)象。當(dāng)給一個(gè)對(duì)象發(fā)送autorelease消息時(shí),實(shí)際上是將該對(duì)象添加到NSAutoreleasePool中。當(dāng)自動(dòng)釋放池被銷(xiāo)毀時(shí),會(huì)向該池中的所有對(duì)象發(fā)送release消息。
- - (NSString *) description
- {
- NSString *description;
- description = [[NSString alloc]
- initWithFormat: @"I am %d years old", 4];
- return ([description autorelease]);7} // description
自動(dòng)釋放池的銷(xiāo)毀時(shí)間:
在使用Foundation庫(kù)工具中,創(chuàng)建和銷(xiāo)毀自動(dòng)釋放池的方法非常明確:
- NSAutoreleasePool *pool;pool = [[NSAutoreleasePool alloc] init];…[pool release];
創(chuàng)建一個(gè)自動(dòng)釋放池時(shí),該池自動(dòng)成為活動(dòng)的池。釋放該池時(shí),其保留計(jì)數(shù)器值歸0,然后該池被銷(xiāo)毀。在銷(xiāo)毀過(guò)程中,該池釋放其包含的所有對(duì)象。
當(dāng)時(shí)用AppKit時(shí),Cocoa定期自動(dòng)為你創(chuàng)建和銷(xiāo)毀自動(dòng)釋放池。通常是在程序處理完當(dāng)前事件(如鼠標(biāo)單擊或按鍵)以后執(zhí)行這些操作。你可以使用任意多的自動(dòng)釋放對(duì)象,當(dāng)不再使用它們時(shí),自動(dòng)釋放池將自動(dòng)為你清理這些對(duì)象。
說(shuō)明:在Xcode自動(dòng)生成的代碼中使用了另一種銷(xiāo)毀自動(dòng)釋放池中對(duì)象的方式:-drain方法,該方法只是清空自動(dòng)釋放池而不銷(xiāo)毀它。-drain方法只適用于Mac OS X 10.4(Tiger)及更高版本。
自動(dòng)釋放池的工作過(guò)程:示例:RetainCount2
- OBJECTIVE-C CODE :RetainCount2
- //
- // Main.m
- // RetainCount2
- // 內(nèi)存管理練習(xí)2: 使用自動(dòng)釋放池
- //
- // Created by Elf Sundae on 10/25/10.
- // Copyright 2010 Elf.Sundae(at)Gmail.com. All rights reserved.
- //
- #import <Cocoa/Cocoa.h>
- @interface RetainTracker : NSObject
- @end
- @implementation RetainTracker
- - (id) init
- {
- if (self = [super init])
- {
- NSLog(@"init: Retain count of %d.",[self retainCount]);
- }
- return self;
- }
- - (void) dealloc
- {
- NSLog(@"dealloc called.Bye Bye.");
- [super dealloc];
- }
- @end
- int main (int argc, const char * argv[])
- {
- // 創(chuàng)建一個(gè)自動(dòng)釋放池
- NSAutoreleasePool *pool;
- pool = [[NSAutoreleasePool alloc] init];
- // 創(chuàng)建(new)一個(gè)RetainTracket對(duì)象
- RetainTracker *tracker = [RetainTracker new]; // count:1
- NSLog(@"%d",[tracker retainCount]);
- // 處于演示目的,retain一個(gè)trancker
- [tracker retain]; // count:2
- NSLog(@"%d",[tracker retainCount]);
- // 向?qū)ο蟀l(fā)送autorelease消息,將該對(duì)象添加到自動(dòng)釋放池pool中
- [tracker autorelease]; // count: still 2
- NSLog(@"%d",[tracker retainCount]);
- // release該對(duì)象以抵消上面的 retain
- [tracker release]; //count: 1
- NSLog(@"%d",[tracker retainCount]);
- NSLog(@"releaseing pool");
- [pool release];
- return 0;
- }
小結(jié):Objective-C學(xué)習(xí)筆記Cocoa生命周期的內(nèi)容介紹完了,希望通過(guò)本文的學(xué)習(xí)能對(duì)你有所幫助!