Cocoa Framework簡(jiǎn)稱(chēng)Cocoa,它是Mac OS X上的快速應(yīng)用程序開(kāi)發(fā)(RAD, Rapid Application Development)框架,一個(gè)高度面向?qū)ο蟮?Object Oriented)開(kāi)發(fā)框架。無(wú)論您是資深的Mac開(kāi)發(fā)人員,還是即將踏入Mac開(kāi)發(fā)世界的新新人類(lèi),Cocoa都是您開(kāi)發(fā)應(yīng)用程序的瑞士軍刀、樂(lè)高積木,它是您構(gòu)建Mac OS X應(yīng)用程序最強(qiáng)大、最高效的工具。值得一提的是,蘋(píng)果公司之所以能夠開(kāi)發(fā)出眾多頂級(jí)軟件,其實(shí)也正是因?yàn)橛兄鳦ocoa這個(gè)秘密武器。Cocoa是Mac OS X上原生支持的應(yīng)用程序開(kāi)發(fā)框架,蘋(píng)果公司強(qiáng)烈推薦所有Mac開(kāi)發(fā)人員使用。
骨灰話(huà)題:Hello World!
相信對(duì)于任何一個(gè)開(kāi)發(fā)人員,Hello World!都應(yīng)該是個(gè)熟悉無(wú)比的東西。那么,Cocoa的Hello World!程序您看得懂么?
- import
- int main (int argc, const char * argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- NSLog(@"Hello, World!");
- [pool drain];
- return 0;
- }
Hmm... 看不明白?不再仔細(xì)看看?還是看不明白?
不論您是否看得明白,首先,和絕大多數(shù)您所見(jiàn)過(guò)的C程序一樣,任何一個(gè)Cocoa應(yīng)用程序都有一個(gè)主函數(shù):
int main(int argc, const char * argv[])
主函數(shù)是應(yīng)用程序的主入口,也是一切開(kāi)始和結(jié)束的地方。是的,沒(méi)錯(cuò)!從表面上看,這些代碼和常見(jiàn)的C程序的代碼沒(méi)有什么區(qū)別,而且事實(shí)上,它們和C程序的代碼的的確確沒(méi)有什么區(qū)別。
接下來(lái)你該問(wèn)我,那什么是Foundation?Foundation.h里裝的又是什么?什么是NSAutoreleasePool?那些中括號(hào)又是什么?...... 別急,在后續(xù)的幾章里我們會(huì)慢慢把這么多陌生的面孔熟悉起來(lái),當(dāng)然我也可以現(xiàn)在就大致的解釋一下:
Foundation全稱(chēng)Foundation Framework,是Cocoa的一個(gè)子開(kāi)發(fā)框架。Foundation里包含了Cocoa中最基本的一些類(lèi),它們?cè)谝粋€(gè)Mac應(yīng)用程序中通常負(fù)責(zé)對(duì)象管理、內(nèi)存管理、容器等相關(guān)數(shù)據(jù)結(jié)構(gòu)的操作。Foundation.h是Foundation的頭文件,一旦引入了這個(gè)頭文件,我們就可以在自己的程序里使用任何在Foundation里聲明的類(lèi)。例如在上述代碼中,NSAutoreleasePool就是Foundation中聲明的一個(gè)類(lèi),它為應(yīng)用程序提供可控制的延遲對(duì)象釋放的內(nèi)存管理。至于那些方括號(hào),它們是Objective-C里最最基本的語(yǔ)法成分,一對(duì)方括號(hào)中間的內(nèi)容表示對(duì)對(duì)象(或類(lèi))進(jìn)行一次方法的調(diào)用。
如果您沒(méi)有聽(tīng)明白,請(qǐng)不要驚慌失措,咱們?cè)谙旅娴膸渍吕锫f(shuō)。另外,Cocoa的國(guó)際音標(biāo)是['koukou]希望大家能把這個(gè)單詞的音讀對(duì)。
我其實(shí)很C
在使用Cocoa進(jìn)行應(yīng)用程序開(kāi)發(fā)的時(shí)候,Objective-C是我們首選的語(yǔ)言。(當(dāng)然,Cocoa也通過(guò)官方、第三方等途徑,構(gòu)建了Cocoa-Ruby、Cocoa-Python等編程語(yǔ)言上的橋接,使無(wú)論是來(lái)自Ruby還是Python世界的開(kāi)發(fā)人員能夠使用他們熟悉的語(yǔ)言快速上手。)不過(guò)在這里,我們還是要首推Objective-C作為一個(gè)Cocoa程序的開(kāi)發(fā)語(yǔ)言,原因有二:
其一、Objective-C實(shí)際上是C的超集。
Objective-C的運(yùn)行環(huán)境庫(kù)(Runtime Library)完全是由C編寫(xiě)的,因此任何一個(gè)Objective-C消息派發(fā)(Message Dispatching),都會(huì)被替換成Objective-C運(yùn)行環(huán)境中的某個(gè)或某幾個(gè)C函數(shù)的調(diào)用。這種機(jī)制為Objective-C帶來(lái)了高效的消息派發(fā)和對(duì)C代碼完全原生的兼容。
其二、整個(gè)Cocoa Framework都是由Objective-C/C實(shí)現(xiàn)的。
因此在開(kāi)發(fā)一個(gè)Cocoa應(yīng)用程序的時(shí)候,Objective-C是我們不二的選擇。
當(dāng)我們說(shuō)到“Objective-C”的時(shí)候,盡管編程模型和C不同,盡管語(yǔ)法看似有些奇怪,但事實(shí)我們所討論的依然還是C語(yǔ)言。因?yàn)镺bjective-C確實(shí)是C,說(shuō)的簡(jiǎn)單些:
Objective-C就是擁有一個(gè)面向?qū)ο髮?Object Oriented Layer)的C。而Objective-C正是通過(guò)從Smalltalk進(jìn)化而來(lái)的這種語(yǔ)法,實(shí)現(xiàn)了對(duì)這個(gè)面向?qū)ο髮拥闹С帧R虼巳绻谑褂肙bjective-C開(kāi)發(fā)一個(gè)Cocoa應(yīng)用程序,同時(shí)又希望在某些代碼的某些位置插入一些C代碼,那就請(qǐng)放心大膽的繼續(xù)吧!
設(shè)計(jì)模式深入骨髓:無(wú)處不在的MVC
在傳統(tǒng)的開(kāi)發(fā)模式中,我們很容易陷入“膠水代碼”的陷阱里。所謂的“膠水代碼”,顧名思義,就是僅僅用來(lái)保持用戶(hù)界面數(shù)據(jù)、狀態(tài)同步的函數(shù)調(diào)用的集合體。這些函數(shù)調(diào)用扯不斷,理還亂,并且使代碼變的非常冗長(zhǎng)、易出錯(cuò)、不易維護(hù)。
為了解決這個(gè)問(wèn)題,Cocoa提供了多個(gè)內(nèi)部機(jī)制:Key-Value Coding(KVC)、Key-Value Observing(KVO)、Key-Value Binding(KVB)。這些機(jī)制通過(guò)規(guī)定了一組通用的Cocoa命名法則、調(diào)用規(guī)則等,實(shí)現(xiàn)了如下功能:
1. 使用一對(duì)高度規(guī)范化的訪(fǎng)問(wèn)方法,獲取以及設(shè)置任何對(duì)象的任何屬性的值(所謂的屬性既可以是個(gè)實(shí)實(shí)在在的成員變量,也可以是通過(guò)一對(duì)成員方法所抽象出的該對(duì)象的一個(gè)性質(zhì))。
2. 通過(guò)繼承一個(gè)特定的方法,并且指定希望監(jiān)視的對(duì)象及希望監(jiān)視的屬性名稱(chēng),就能在該對(duì)象的指定屬性的值發(fā)生改變時(shí),得到一個(gè)“通知”(盡管這不是一個(gè)真正意義上的通知),并且得到相關(guān)屬性的值的變化(原先的值和改變后的新值)。
3. 通過(guò)一個(gè)簡(jiǎn)單的函數(shù)調(diào)用,使一個(gè)視圖對(duì)象的一個(gè)指定屬性隨時(shí)隨地都和一個(gè)控制器對(duì)象或模型對(duì)象的一個(gè)指定屬性保持同步。
一個(gè)開(kāi)發(fā)者可以利用這些功能,將自己創(chuàng)建的類(lèi)寫(xiě)的很范化、很通用,然后通過(guò)KVB將多個(gè)視圖“綁定”到一個(gè)或多個(gè)控制器上,再將控制器綁定到最底層的數(shù)據(jù)模型上。這樣一來(lái),任何一個(gè)視圖上的改變都會(huì)通過(guò)KVC被“壓”到控制器里,然后又通過(guò)KVC從控制器“壓”到數(shù)據(jù)模型里。當(dāng)數(shù)據(jù)模型中的值發(fā)生改變時(shí),一個(gè)或多個(gè)控制器又會(huì)得到KVO的“通知”,接著只要被綁定了的視圖又會(huì)得到這一個(gè)或多個(gè)控制器的KVO“通知”。這樣以來(lái),開(kāi)發(fā)者只需要在適當(dāng)?shù)臅r(shí)候告訴Cocoa,什么對(duì)象的什么值該和什么對(duì)象的什么值綁定,就可以了,其余數(shù)據(jù)更新、格式化等工作Cocoa都會(huì)替你完成。
是不是很方便呢?
Easy Life之一:內(nèi)存管理
內(nèi)存管理是令很多開(kāi)發(fā)人員頭大的問(wèn)題,在Cocoa中,內(nèi)存管理是通過(guò)引用計(jì)數(shù)器模型完成的。
Cocoa中的每個(gè)對(duì)象都擁有一個(gè)引用計(jì)數(shù)器,用來(lái)維持自己的生命周期。每當(dāng)一個(gè)對(duì)象需要“使用”或“占有”另一個(gè)對(duì)象的時(shí)候,它通過(guò)向該對(duì)象發(fā)送一個(gè)retain消息來(lái)對(duì)該對(duì)象的引用計(jì)數(shù)器進(jìn)行自增,而當(dāng)它不再需要(或使用完)該對(duì)象的時(shí)候,它通過(guò)向該對(duì)象發(fā)送一個(gè)release消息來(lái)對(duì)該對(duì)象的引用計(jì)數(shù)器進(jìn)行自減。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)器自減到零時(shí),該對(duì)象就會(huì)被釋放。
下面我們來(lái)看一個(gè)例子,例如:
- NSString *aString = [[NSString alloc] initWithString:@"This is a demo."];
這段代碼會(huì)創(chuàng)建一個(gè)NSString對(duì)象,并對(duì)其進(jìn)行初始化。當(dāng)一個(gè)對(duì)象被創(chuàng)建的時(shí)候,它的引用計(jì)數(shù)器會(huì)被設(shè)為1。因此當(dāng)您不再需要該對(duì)象,只要直接對(duì)其發(fā)送release消息,它就會(huì)被直接析構(gòu)。當(dāng)您有別的代碼塊也需要使用這個(gè)NSString時(shí),您可以對(duì)這個(gè)NSString對(duì)象調(diào)用一次retain來(lái)增加它的引用計(jì)數(shù)器:
[aString retain]; 這個(gè)時(shí)候,它的引用計(jì)數(shù)器值就是2了。當(dāng)您使用完該對(duì)象時(shí),如果您對(duì)該對(duì)象調(diào)用過(guò)retain,那就應(yīng)該“對(duì)稱(chēng)”地調(diào)用一次release。這時(shí)一種基本地編程規(guī)范,我將它稱(chēng)為“誰(shuí)retain,誰(shuí)release”。
- [aString release];
調(diào)用完以后,引用計(jì)數(shù)器再次回到1。最后,當(dāng)我們徹底不需要這個(gè)對(duì)象的時(shí)候,我們可以這么做:
- [aString release];
- aString = nil;
上兩行代碼中,第一句會(huì)負(fù)責(zé)將這個(gè)NSString對(duì)象析構(gòu),第二句會(huì)負(fù)責(zé)將原來(lái)指向這個(gè)NSString對(duì)象的指針(NSString *aString)“歸零”,因?yàn)椤耙爸羔槨彪S時(shí)可能造成您程序的異常及崩潰。
聽(tīng)著是不是挺簡(jiǎn)單?
當(dāng)然也有稍微復(fù)雜一些的情況,話(huà)說(shuō)一開(kāi)始我們有提到一個(gè)叫作NSAutoreleasePool的類(lèi)吧?NSAutoreleasePool是Cocoa內(nèi)存管理機(jī)制里很重要的一個(gè)環(huán)節(jié)。我們?cè)诒局罢l(shuí)retain,誰(shuí)release”的對(duì)象使用的大前提下,經(jīng)常會(huì)碰到這么一個(gè)問(wèn)題,那就是我們希望返回一個(gè)在局部中創(chuàng)建的對(duì)象:
- (NSString)demoString
- {
- NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
- return result;
- }
在“誰(shuí)retain,誰(shuí)release”的原則下,上面的代碼顯然只負(fù)責(zé)了retain(alloc調(diào)用等效于retain),但是沒(méi)有負(fù)責(zé)release,因此這么寫(xiě)可能會(huì)造成內(nèi)存泄露,因?yàn)檎{(diào)用這個(gè)方法(或這個(gè)API)的代碼段并不知道究竟是否需要負(fù)責(zé)釋放這個(gè)方法(或這個(gè)API)的返回值。
但是如果我們將它直接release了:
- (NSString)demoString
- {
- NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
- [result release];
- return result;
- }
那return的將會(huì)是個(gè)“野指針”(或者如果你干的足夠干凈,return的是個(gè)零指針),不是我們需要的值。因此我們需要一個(gè)能夠延遲釋放,并且能夠自動(dòng)釋放的機(jī)制。于是,人們發(fā)明了名叫NSAutoreleasePool的又一個(gè)輪子,而代碼則變成了這個(gè)樣子:
- (NSString)demoString
- {
- NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
- [result autorelease];
- return result;
- }
在對(duì)一個(gè)對(duì)象發(fā)送了autorelease之后,這個(gè)對(duì)象不會(huì)被立即釋放,而是被“登記”到了離它最近的一個(gè)NSAutoreleasePool對(duì)象上。當(dāng)該NSAutoreleasePool被清空或釋放的時(shí)候,這個(gè)“登記”了的對(duì)象才會(huì)被真正發(fā)送一個(gè)release消息。
Easy Life之二:容器
容器是讓多數(shù)程序員又愛(ài)又恨的東西。在Cocoa中,容器是如此的簡(jiǎn)單易用以至于您一旦用過(guò),就會(huì)對(duì)它們“愛(ài)不釋手”。Cocoa中的容器類(lèi)主要有這么幾個(gè):NSString、NSArray、NSDictionary、NSSet和NSIndexSet等,它們都是Foundation Framework的一部分。
為什么人們會(huì)對(duì)Cocoa的容器“愛(ài)不釋手”呢?
原因一:NSArray、NSDictionary、NSSet都不強(qiáng)制其內(nèi)部元素類(lèi)型的一致性。舉個(gè)簡(jiǎn)單的例子:
NSString *aString = [NSString stringWithString:@"This is a demo."];
NSNumber *aNumber = [NSNumber numberWithInteger:0];
NSArray *anArray = [[NSArray alloc] initWithObjects:aString, aNumber, nil];
在上述例子中,我們首先建立了一個(gè)NSString對(duì)象,然后又建立了一個(gè)NSNumber對(duì)象,最后我們將這兩個(gè)NSString和NSNumber對(duì)象都“塞”到了一個(gè)NSArray對(duì)象中。
有夠爽吧?連想都別想,什么東西都能往里面裝(基本類(lèi)型、結(jié)構(gòu)體除外)!
原因二:容器類(lèi)的“可修改”和“不可修改”
上面我們展示的NSString、NSArray、NSDictionary、NSSet以及NSIndexSet等,都是容器的“不可修改”的版本。所謂的“不可修改”,指的是這個(gè)容器一旦被創(chuàng)建以后,我們就不可以通過(guò)代碼修改它的集合。那如果我們需要修改(例如添加、刪除、替換)這些容器的元素,該怎么辦呢?
Cocoa中幾乎所有的容器類(lèi),都提供了另外一個(gè)“可修改”的版本。例如:繼承自NSString的NSMutableString、繼承自NSArray的NSMutableArray、繼承自NSDictionary的NSMutableDictioanry、繼承自NSSet的NSMutableSet以及繼承自NSIndexSet的NSMutableIndexSet等。這些“可修改”的版本提供了簡(jiǎn)單直觀(guān)的方法,用來(lái)修改其內(nèi)部的元素。例如:
- NSString *aString = [NSString stringWithString:@"This is a demo."];
- NSNumber *aNumber = [NSNumber numberWithInteger:0];
- NSMutableArray *aMutableArray = [NSMutableArray array];
- [aMutableArray addObject:aString];
- [aMutableArray addObject:aNumber];
- [aMutableArray removeObjectAtIndex:0];
- [aMutableArray removeAllObjects];
在上述代碼中,第一、二行建立了一個(gè)NSString對(duì)象和一個(gè)NSNumber對(duì)象。第三行建立了一個(gè)NSMutableArray對(duì)象(也就是一個(gè)“可修改”的NSArray對(duì)象)。第四、五行通過(guò)-addObject:方法分別將第一、二行建立的NSString對(duì)象和NSNumber對(duì)象加入了這個(gè)“可修改”的NSArray里。第六行則是根據(jù)我們給定的索引號(hào)0,刪除了數(shù)組中的第一個(gè)元素。第七行的-removeAllObjects最后一口氣將數(shù)組中存在的所有元素統(tǒng)統(tǒng)刪除(置空數(shù)組)。
是不是感覺(jué)一切都變的簡(jiǎn)單了許多呢?
那就趕快拿起Xcode,開(kāi)始享受Cocoa編程所帶來(lái)的快樂(lè)吧!
粉絲看Cocoa
“Simple things simple, complex things possible.”(簡(jiǎn)單的事情更簡(jiǎn)單,復(fù)雜的事情也成為可能。)
“C++ is to C as Lung Cancer is to Lang.”(C++之于C正如肺癌之于肺。)
“We thought we would see how hard it would be to switch the code we had to use Cocoa Bindings. We rewrote everything in a day or two - I think we deleted over a thousand lines of code that just wasn't needed any more.”(當(dāng)初我們認(rèn)為將原有的代碼遷移到使用Cocoa Bindings的代碼是一件非常艱苦的活。但事實(shí)上我們?cè)趦H僅一兩天內(nèi)就重寫(xiě)了所有的東西——我想我們刪除了有超過(guò)一千行因?yàn)镃ocoa Bindings而不再需要了的代碼。)
“Without Cocoa Bindings, it would have taken another four or five months, maybe more, to finish Delicious Library.”(要是沒(méi)有Cocoa Bindings,完成Delicious Library將要再花上四、五個(gè)月的時(shí)間,或許更長(zhǎng)。)
“With Cocoa, we were able to accomplish a lot more, more quickly.”(有了Cocoa,我們能夠?qū)崿F(xiàn)更多,而且更迅速。)
“I want to marry Core Data and have its children.”(我想和Core Data結(jié)婚,并有它的孩子。)
【編輯推薦】
聯(lián)系客服