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