Objective-C 2.0 with Cocoa Foundation --- 2,從Hello,World!開始

原文鏈接:http://www.cnblogs.com/yaski/


2,從Hello,World!開始

本系列講座有着很強的前後相關性,如果你是第一次閱讀本篇文章,爲了更好的理解本章內容,筆者建議你最好從本系列講座的第1章開始閱讀,請點擊這裏 

現在筆者假設大家已經有了開發的環境。好了,我們開始構築我們的第一個程序。

在開始第一個程序之前,筆者需要提醒大家一下,如果手裏面有開發環境的話並且是第一次親密接觸Xcode的話,爲了可以熟悉開發環境,強烈建議按照筆者的步驟一步一步的操作下去。儘管如此,筆者還是爲大家準備了已經做好的代碼,點擊這裏下載。

2.1,構築Hello, World

第一步,啓動Xcode。初次啓動的時候,也許會彈出一個“Welcome to Xcode”的一個對話框。這個對話框和我們的主題沒有關係,我們可以把它關掉。

第二步,選擇屏幕上部菜單的“File->New Project”,出現了一個讓你選擇項目種類的對話框。你需要在對話框的左邊選擇“Command Line Utility” ,然後在右邊選擇“Foundation Tool”,然後選擇“Choose...”按鈕。如圖2.1所示。

 

 圖2-1,新建項目

注意也許有人會問,你不是要講解iPhone的開發,那麼爲什麼不選擇“iPhone OS”下面的“Application”呢?
是這樣的,在這個系列當中,筆者主要側重於Objective-C的語法的講解,爲了使得講解簡單易懂,清除掉所有和要講解的內容無關的東西,所以筆者在這裏只是使用最簡單的命令行。

 

 第三步,Xcode會提問你項目的名字,在“Save As”裏面輸入“02-Hello World”,然後選擇“Save”。如圖2-2所示

 

圖2-2,輸入項目的名字

第四步,得到一個如圖2-3所示的一個畫面。嘗試一下用鼠標分別點擊左側窗口欄裏面的“02-Hello World”,“Source”.“Documentation”,“External Frameworks and Libraries”,“Products”,然後觀察一下右邊的窗口都出現了什麼東西。一般來說,“02-Hello World”就是項目的名字下面是項目所有的文件的列表。項目下面的子目錄分別是和這個項目相關的一些虛擬或者實際上的目錄。爲什麼我說是虛擬的呢?大家可以通過Finder打開你的工程文件的目錄,你會發現你的所有文件居然都在根目錄下,根本就不存在什麼“Source”之類的目錄。

 

圖2-3,項目瀏覽窗口 

第五步,選擇屏幕上方菜單的“Run”然後選擇“Console”,出現瞭如圖2-4所示的畫面,用鼠標點擊窗口中間的“Build and Go”按鈕。

 

圖2-4,運行結果畫面 

如果不出什麼意外的話,大家應該看到我們熟悉得不能再熟悉的“Hello Wolrd!” 。由於我們沒有寫任何的代碼,所以從理論上來說,這部分代碼不應該出現編譯錯誤。好的,從下面開始,筆者要開始對這個Hello World裏面的一些新鮮的東西進行講解。

2.2,頭文件導入

在Java或者C/C++裏面,當我們的程序需要引用外部的類或者方法的時候,需要在程序源文件中包含外部的類以及方法的包(java裏面的jar package)或者頭文件(C/C++的.h), 在Objective-C裏面也有相類似的機制。筆者在這一節裏面將要向大家介紹在Objective-C裏面,頭文件是怎樣被包含進來的。

請同學們到Xcode開發環境的左側窗口裏面,點擊Source文件夾,然後就在右側部分看到了代碼源文件的列表,找到02-Hello World.m之後單擊會在Xcode的窗口裏面出現,雙擊鼠標代碼會在一個新窗口出現,請同學們按照這種方法打開"02-Hello World.m"。

對於Java程序來說,源程序的後綴爲.java,對於C/C++代碼來說,後綴爲c/cpp,現在我們遇到了.m。當Xcode看到了.m文件之後,就會把這個文件當作Objective-C文件來編譯。同學們也許會猜到,當Xcode遇到c/cpp,或者java的時候也會對應到相應的語言的。

好的,我們順便提了一下Xcode對.m文件的約定,現在我們開始從第一行代碼講起,請參看下列代碼:

 1 #import <Foundation/Foundation.h>
 2 
 3 int main (int argc, const char * argv[]) {
 4     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 5 
 6     // insert code here
 7     NSLog(@"Hello, World!");
 8     [pool drain];
 9     return 0;
10 }
11 

有過C/C++經驗的同學看到第一行,也許會覺得有些親切;有過Java經驗的同學看到第一行也許也會有一種似曾相識的感覺。同學們也許猜到了這是幹什麼用的,沒錯,這個正是頭文件。不過,在C/C++裏面是#include,在java裏面是import,這裏是#import。

在C/C++裏面會有#include互相包含的問題,這個時候需要#ifdef來進行編譯的導向,在Xcode裏面,同學們可以"放心的"包含各種東西,這個沒有關係,因爲我們的編譯器有足夠的“聰明”,因爲同一個頭文件只是被導入一次。除了#import變得聰明瞭一點之外,和#include的功能是完全一樣的。

我們再來看看我們的另外一個新的朋友---Foundation.h。這個是系統框架Foundation framework的頭文件,有了它你可以免費的獲取系統或者說蘋果公司爲你精心準備的一系列方便你使用的系統功能,比如說字符串操作等等。Foundation框架從屬於Cocoa框架集,Cocoa的另外一個框架爲Application Kit,或者是UIKit,其中前者的應用對象爲MAC OS,後者的應用對象爲iPhone OS。本系列入門指南將只是使用Foundation,因爲筆者需要向同學們介紹Objective-C的基本使用方法,爲了避免過多的新鮮東西給同學們造成閱讀上的困難,所以命令行就已經足夠了。

說到這裏,筆者需要澄清一點,其實MAC OS的Cocoa和iPhone的Cocoa是不一樣的,可以說,其中iPhone是MAC OS的一個子集。

2.3,main函數

有過C/C++或者java經驗的同學們對第3行代碼應該很熟悉了,是的大家都一樣主程序的入口都是main。這個main和C/C++語言裏面的main是完全一樣的,和java語言在本質上也是完全一樣的。因爲Objective-C完全的繼承了C語言的特性。確切的說,不是說Objective-C和C語言很相似,而是Objective-C和C語言是完全兼容的。

關於main函數是幹什麼用的,筆者就不在這裏羅嗦了,有興趣的同學可以找一本C語言的書看看。

2.4,關於NSAutoreleasePool

自己動手,豐衣足食---

在第4行,我們遇到了另外一個新鮮的東西,這就是NSAutoreleasePool。

讓我把這個單詞分爲三部分,NS,Autorelease和Pool。

當我們看到NS的時候,也許不知道是代表着什麼東西。NS其實只是一個前綴,爲了避免命名上的衝突。NS來自於NeXTStep的一個軟件,NeXT Software的縮寫,NeXT Software是Cocoa的前身,一開始使用的是NS,爲了保持兼容性所以NS一直得以保留。在多人開發的時候,爲了避免命名上的衝突,開發組的成員最好事先定義好各自的前綴。但是,最好不要有同學使用NS前綴,這樣會讓其他人產生誤解。

略微有些遺憾的是,Objective-C不支持namespace關鍵字,不知道後續的版本是否會支持。

下面我們討論一下Autorelease和Pool。

程序在執行的時候,需要向系統申請內存空間的,當內存空間不再被使用的時候,毫無疑問內存需要被釋放,否則有限的內存空間會很快被佔用光光,後面的程序將無法得到執行的有效內存空間。從計算機技術誕生以來,無數的程序員,我們的無數先輩都在爲管理內存進行努力的工作,發展到現在,管理內存的工作已經得到了非常大的完善。

在Objective-C或者說Cocoa裏面,有三種內存的管理方式。

第一種,叫做“Garbage Collection”。這種方式和java類似,在你的程序的執行過程中,始終有一個高人在背後準確地幫你收拾垃圾,你不用考慮它什麼時候開始工作,怎樣工作。你只需要明白,我申請了一段內存空間,當我不再使用從而這段內存成爲垃圾的時候,我就徹底的把它忘記掉,反正那個高人會幫我收拾垃圾。遺憾的是,那個高人需要消耗一定的資源,在攜帶設備裏面,資源是緊俏商品所以iPhone不支持這個功能。所以“Garbage Collection”不是本入門指南的範圍,對“Garbage Collection”內部機制感興趣的同學可以參考一些其他的資料,不過說老實話“Garbage Collection”不大適合適初學者研究。

第二種,叫做“Reference Counted”。就是說,從一段內存被申請之後,就存在一個變量用於保存這段內存被使用的次數,我們暫時把它稱爲計數器,當計數器變爲0的時候,那麼就是釋放這段內存的時候。比如說,當在程序A裏面一段內存被成功申請完成之後,那麼這個計數器就從0變成1(我們把這個過程叫做alloc),然後程序B也需要使用這個內存,那麼計數器就從1變成了2(我們把這個過程叫做retain)。緊接着程序A不再需要這段內存了,那麼程序A就把這個計數器減1(我們把這個過程叫做release);程序B也不再需要這段內存的時候,那麼也把計數器減1(這個過程還是release)。當系統(也就是Foundation)發現這個計數器變成了0,那麼就會調用內存回收程序把這段內存回收(我們把這個過程叫做dealloc)。順便提一句,如果沒有Foundation,那麼維護計數器,釋放內存等等工作需要你手工來完成。

這樣做,有一個明顯的好處就是,當我們不知道是A先不使用這段內存,還是B先不使用這段內存的時候,我們也可以非常簡單的控制內存。否則,當我們在程序A裏面釋放內存的時候,還需要看看程序B是否還在使用這段內存,否則我們在程序A裏面釋放了內存之後,可憐的程序B將無法使用這段內存了。這種方式,尤其是在多線程的程序裏面很重要,如果多個線程同時使用某一段內存的時候,安全的控制這些內存成爲很多天才的程序員的夢魘。

如果有同學搞過COM的話,那麼應該對Release/AddRef很熟悉了,其實Obejctive-C和他們的機制是一樣的。

接下來,我需要解釋一下Autorelease方式。上述的alloc->retain->release->dealloc過程看起來比較令人滿意,但是有的時候不是很方便,我們代碼看起來會比較羅嗦,這個時候就需要Autorelease。Autorelease的意思是,不是立即把計數器減1而是把這個過程放在線程裏面加以維護。當線程開始的時候,需要通知線程(NSAutoreleasePool),線程結束之後,才把這段內存釋放(drain)。Cocoa把這個維護所有申請的內存的計數器的集合叫做pool,當不再需要pool(水池)的時候就要drain(放水)。

筆者想要說的是,雖然iPhone支持Autorelease但是我們最好不要使用。因爲Autorelease方式從本質上來說是一種延遲釋放內存的機制,手機的空間容量有限,我們必須節約內存,確定不需要的內存應該趕快釋放掉,否則當你的程序使用很多內存的情況下也許會發生溢出。這一個習慣最好從剛剛開始學習使用Objective-C的時候就養成,否則長時間使用Autorelease會讓你變得“懶散”,萬一遇到問題的時候,解決起來會非常耗費時間的。所以,還是關於內存管理,我們還是自己動手,豐衣足食。當然筆者不是說絕對不可以使用,而是當使用Autorelease可以明顯減低程序複雜度和易讀性的時候,還是考慮使用一下換一下口味。

說到這裏,可能有的同學已經開始發暈了,認爲這個東西比較難以理解。是的,筆者在這裏只是介紹一個大概的東西,在這裏只要瞭解計數器的概念就可以了,筆者將在隨後的章節裏面對這個功能加以詳細論述,請同學們放心,這個東西和Hello World一樣簡單。

關於Pool

在使用Pool的時候,也要記住系統給你的Pool的容量不是無限大的,從這一點來說和在現實世界的信用卡比較相似。

你可以在一定程度透支,但是如果“忘記掉”信用卡的額度的話,會造成很大的系統風險。

第三種,就是傳統而又原始的C語言的方式,筆者就不在這裏敘述了。除非你在Objective-C裏面使用C代碼,否則不要使用C的方式來申請和釋放內存,這樣會增加程序的複雜度。

線程是什麼東西?線程指的是進程中一個單一順序的控制流。它是系統獨立調度和分派的基本單位。同一進程中的多個線程將共享該進程中的全部系統資源,比如文件描述符和信號處理等等。 一個進程可以有很多線程,每個線程並行執行不同的任務。

2.5,關於[[NSAutoreleasePool alloc] init];

關於程序第4行等號右邊出現的括弧以及括弧裏面的內容,筆者將在後續的章節裏面介紹。在這裏,同學們可以理解爲,通過告訴Objective-C編譯器[[NSAutoreleasePool alloc] init],編譯器就會成功的編譯生成NSAutoreleasePoo對象的代碼就可以了。

2.6,Objective-C裏面的註釋 

同學們在第6行看到了//的註釋,這個和C++以及Java是一樣的,表示這一行的內容是註釋,編譯器將會忽略這一行的內容。筆者在上面說過Objective-C完全兼容C語言,所以C語言裏面傳統的/**/在Objective-C裏面也是有效的。

2.7,命令行輸出

第7行,我們看到了NSLog這個函數。NS上面已經講過了,我們都知道Log是什麼意思,那麼這段代碼的意思就是輸出一個字符串,Xcode的代碼生成器自己把字符串定義爲“Hello, World!”。NSLog相當於C語言裏面的printf,由於我們是在使用Objective-C所以筆者將會和同學們一起,在這裏暫時忘記掉我們過去曾經熟悉的printf。

有眼光銳利的同學會發現在字符串的前面多了一個@符號,這是一個什麼東西呢?

如前所述,Objective-C和C是完全兼容的,但是NSLog這個函數需要的參數是NSString,這樣就產生了一個問題,如果使用C的字符串方式的話,爲了保持和C的兼容性編譯器將會把字符串理解爲C的字符串。爲了和C的字符串劃清界限,在C的字符串前面加上@符號,Objective-C的編譯器會認爲這是一個NSString,是一個NSLog喜歡的參數。

爲什麼NSLog或者Cocoa喜歡使用NSString? 因爲NSString封裝了一系列的字符串的方法比如字符串比較,字符串和數字相互轉換等等的方法,使用起來要比C的字符串方便的多。

2.8,本章總結

非常感謝同學們耐心的看到這裏!

通過理解本章的內容,同學們應該可以使用Xcode創建一個命令行的工程,理解.m文件的基本要素,理解內存的管理方法的思路,還有Objective-C的註釋的寫法,以及命令行的輸出方法。

是不是很簡單又很有樂趣呢?筆者將會盡最大努力把看起來複雜的東西講解的簡單一些,並且真心的希望大家可以從中找到樂趣。

下一章我們要講解一個同學們已經很熟悉的一個概念,Class也就是類。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章