深入iPhone開發(fā):應用程序核心探秘
每個iPhone應用程序都基于UIKit框架而構(gòu)建,因此擁有同樣的核心架構(gòu)。UIKit提供運行應用程序的關(guān)鍵對象并且協(xié)調(diào)用戶輸入處理和屏幕內(nèi)容顯示。應用程序彼此之間區(qū)分開來的地方在于如何配置這些缺省對象和它們?nèi)绾伟炎远x對象整合到它們的應用程序用戶界面和行為中。
51CTO推薦專題:iPhone應用程序開發(fā)初探
盡管定制你的應用程序用戶界面和基本行為發(fā)生在你的自定義代碼中,仍然有很多定制必須使用在應用程序的***層。因為這些應用層的定制影響了你的應用程序和系統(tǒng)以及其它安裝程序之間的交互方式,理解什么時候該采取行動而什么時候默認行為已經(jīng)足夠這一點很重要。本文提供了一個核心應用程序架構(gòu)和高級別定制點方面的總體描述來幫助你做出決定。
核心應用程序架構(gòu)
從你的程序被用戶啟動,到它退出,UIKit框架管理著大多數(shù)應用程序關(guān)鍵基礎結(jié)構(gòu)。一個iPhone應用程序不斷從系統(tǒng)中接收事件并必須響應這些事件。接收事件是UIApplication 對象的工作但是響應這些事件是你自定義代碼的責任。為了理解你在哪些地方需要響應事件,其實,這對理解一點整個應用程序生命周期和事件循環(huán)也有幫助。下面的章節(jié)描述了這些周期并且還提供了一些貫穿iPhone應用程序開發(fā)的核心設計模式的總結(jié)。(相關(guān)推薦:專訪最牛iPhone開發(fā)團隊:走進移動開發(fā))
應用程序生命周期
應用程序生命周期構(gòu)成發(fā)生在你的應用程序啟動和退出期間的事件序列。在iPhone 操作系統(tǒng)中, 用戶通過點擊桌面上的菜單啟動你的應用程序。在點擊發(fā)生之后的短時間內(nèi),系統(tǒng)顯示一些過渡圖片并開始通過調(diào)用main函數(shù)啟動你的應用程序。從這一點開始,一大堆的初始化工作被移交給UIKit,它將加載應用程序的用戶界面并準備好它的事件循環(huán)。在事件循環(huán)期間,UIKit 協(xié)調(diào)你自定義對象的事件交付和應用程序發(fā)出命令的響應。當用戶執(zhí)行一個動作會讓你的應用程序退出時,UIKit通知你的應用程序并開始這個結(jié)束過程。
圖1-1 描繪了一個iPhone應用程序的簡單生命周期。這個圖顯示了應用程序啟動到退出期間發(fā)生的事件序列。在初始化和結(jié)束階段,UIKit發(fā)送特定的消息給應用程序代理對象以便其知道發(fā)生了什么。在事件循環(huán)階段,UIKit分發(fā)事件給你的應用程序自定義事件處理器。處理初始化和結(jié)束事件在“Initialization and Termination,” 中描述,而事件處理過程在“The Event-Handling Cycle” 中介紹,在后面的章節(jié)將覆蓋更多的細節(jié)。
圖 1-1 應用程序生命周期
Main函數(shù)
在iPhone應用程序里, main函數(shù)功能被最小化了。大部分實際工作是在UIApplicationMain 函數(shù)中完成的。 當你在Xcode中開始一個新的應用程序項目時,每個項目模版都提供了一個標準main函數(shù)實現(xiàn)如同在 “Handling Critical Application Tasks.”里的那個。Main函數(shù)只做了三件事: 創(chuàng)建了一個自釋放池(autorelease pool),調(diào)用UIApplicationMain,然后釋放autorelease pool。 除了很少的特例,你不應該修改它。
- iPhone應用程序的main函數(shù)
- #import <UIKit/UIKit.h>
- int main(int argc, char *argv[])
- {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
- }
注意: 自釋放池用在內(nèi)存管理中。它是一個Cocoa機制用來延遲在一個函數(shù)體內(nèi)創(chuàng)建的對象的釋放。更多信息參見Memory Management Programming Guide for Cocoa。對于和自釋放池相關(guān)的iPhone應用程序特定的內(nèi)存管理指南,參見“Allocating Memory Wisely.”
上述列表中的中心部分UIApplicationMain函數(shù)采用了4個參數(shù)并使用它們來初始化應用程序。盡管你沒必要更改傳入?yún)?shù)的默認值,還是值得解釋一下它們起動應用程序時的用途。除了argc 和 argv 參數(shù)外,這個函數(shù)采用兩個字符串參數(shù)來識別基本類(也就是,應用程序?qū)ο箢悾┖蛻贸绦虼眍?。如果這個基本類字符串為空,UIKit使用UIApplication類作為缺省值。
如果應用程序代理類為空,UIKit假設它為從你的應用程序主nib文件加載的對象其中之一(對于使用Xcode模版創(chuàng)建的應用程序)。 設置這些參數(shù)任意一個為非空數(shù)值將導致UIApplicationMain 函數(shù)在應用程序啟動時創(chuàng)建一個相應類實例并為聲明的目的使用它。這樣,如果你的應用程序使用一個自定義的UIApplication子類(不推薦這樣,但是當然是可能的),你將在第三個參數(shù)中指定你自定義類的名字。
#p#
應用程序代理
監(jiān)控你的應用程序的高層行為是應用程序代理對象的職責,也就是你提供的自定義對象。代理是用來避免子類化復雜UIKit對象的一個機制,比如缺省的UIApplication對象。和使用子類化以及重寫父類方法相反,你無需修改就可以使用這個復雜對象而把自定義的代碼放在代理類中。當感興趣的事件發(fā)生時,這個復雜對象發(fā)送消息給你的代理對象。你可以使用這些“鉤子”來執(zhí)行自定義代碼并實現(xiàn)你需要的行為。
重要: 這個代理設計模式是用來節(jié)約你創(chuàng)建應用程序的時間和精力的。所以理解這個模式很重要。想對iPhone應用程序采用的關(guān)鍵設計模式有個總體了解,請參見“Fundamental Design Patterns.” 關(guān)于代理和其他UIKit設計模式的更詳細的描述,請參見Cocoa Fundamentals Guide.
應用程序代理對象負責處理一些關(guān)鍵的系統(tǒng)消息而且在每個iPhone應用程序中必須存在。這個對象可以是任何你喜歡的類實例,只要它采用了UIApplicationDelegate 協(xié)議。 這個協(xié)議的方法定義了應用程序生命周期掛載的鉤子,同時也是你實現(xiàn)自定義行為的途徑。盡管你不需要實現(xiàn)所有的方法,每個應用程序代理應該實現(xiàn)在“Handling Critical Application Tasks.”中描述的方法。
主Nib文件
另外一個初始化時期發(fā)生的任務是加載應用程序的主nib文件。如果應用程序信息property list (Info.plist) 文件包含了NSMainNibFile關(guān)鍵值, 作為初始化過程的一部分,UIApplication 對象加載這個關(guān)鍵值指定的nib文件。主nib文件是為你自動加載的唯一nib文件;但是,你可以按照需要加載其他的nib文件。
Nib 文件是基于磁盤的資源文件,保存了一個或多個對象的一份快照。 一個iPhone應用程序的主nib文件通常包含一個窗口對象,應用程序代理對象,和可能一個或多個其他管理這個窗口的關(guān)鍵對象。加載一個nib文件重新構(gòu)成nib文件中的對象,從它的磁盤表示轉(zhuǎn)換成一個實際的可以被你的應用程序操作的內(nèi)存版本。
從nib文件中加載的對象和你編程序創(chuàng)建的對象沒有區(qū)別。不過,對于用戶界面而言,圖形化的創(chuàng)建和用戶界面相關(guān)聯(lián)的對象并存放在nib文件中(使用Interface Builder)比編程實現(xiàn)要簡便直觀得多。
#p#
事件處理循環(huán)
當UIApplicationMain 函數(shù)初始化了應用程序之后,它起動必要的基礎組件來管理這個應用程序的事件和繪制循環(huán),這在圖Figure 1-2中描述。當用戶和設備交互時,iPhone OS 偵測觸摸事件并把它們放到應用程序事件隊列中。UIApplication對象的事件處理組件從事件隊列頂部提取每個事件并遞交給最合適的對象來處理它。比如,一個發(fā)生在一個按鈕上的觸摸事件將會被遞交給相應的按鈕對象。事件也可以被遞交給控制器對象(controller objects)和其他不是直接負責處理該觸摸事件的對象。
圖 1-2 事件和繪畫循環(huán)
在iPhone OS 多點觸摸事件模型中,觸摸數(shù)據(jù)被包裝在一個簡單的事件對象中(UIEvent)。為了跟蹤單獨的觸摸動作,事件對象中包含了觸摸對象(UITouch),每一個代表一個手指觸摸了屏幕。當這個用戶把手指放在屏幕上,并四處移動它們,***從屏幕上移開,系統(tǒng)會在相應的觸摸對象中報告每個手指的變化。
當應用程序啟動時,系統(tǒng)為這個應用程序創(chuàng)建一個進程和線程。這個初始線程成為應用程序主線程, UIApplication 對象就在這里建立主運行循環(huán)(main run loop)并配置應用程序的事件處理編碼。圖1-3 顯示了事件處理編碼和主運行循環(huán)之間的關(guān)系。系統(tǒng)發(fā)送的觸摸事件被排隊直到它們能被應用程序的主循環(huán)處理。
圖1-3 在主運行循環(huán)中處理事件
注意: 一個運行循環(huán)監(jiān)控一個給定的執(zhí)行線程的輸入源。當一個輸入源有數(shù)據(jù)處理時,這個運行循環(huán)喚醒線程并把控制權(quán)派發(fā)給輸入源處理器。當處理完成時,控制權(quán)返回運行循環(huán),繼續(xù)下一個事件或者如果沒有什么事情做的話就讓這個線程休眠。你可以安裝你自己的輸入源,包括端口和時鐘,在一個運行循環(huán)中使用基礎框架中的NSRunLoop 類。
UIApplication用一個輸入源對象配置主運行循環(huán)來處理觸摸事件,把它們分發(fā)給合適的響應者對象。一個響應者對象是從UIResponder 類繼承而來并且實現(xiàn)了一個或多個處理觸摸事件不同階段的方法。應用程序中的響應者對象包括UIApplication實例,UIWindow,UIView,和所有UIView子類。 應用程序通常分發(fā)事件給代表應用程序的主窗口的UIWindow 對象。這個窗口對象,依次,轉(zhuǎn)發(fā)這個事件給它的***響應者first responder,這通常是發(fā)生觸摸事件的視圖對象(UIView)。
除了定義處理事件的方法之外,UIResponder類還定義了響應者鏈的程序結(jié)構(gòu),這是一個協(xié)同事件處理的Cocoa機制。響應者鏈是應用程序中的一個響應者對象連接序列,通常從***響應者開始。如果***響應者對象不能處理這個事件,它傳遞給響應鏈中的下一個。這個消息繼續(xù)回溯響應鏈-給更高級別的響應者對象比如窗口,應用程序,和應用程序代理-直到事件被處理。如果事件最終仍然未被處理,則被拋棄。
處理事件的響應者對象傾向于在移動中設置一系列程序動作而導致應用程序重畫所有或它的用戶界面的一部分(以及其它可能的輸出,比如播放聲音)。例如,一個控制器對象 (也就是,一個UIControl子類),通過發(fā)送一個動作消息給另外一個對象來處理一個事件,通常這個控制器管理著當前激活視圖集。當處理當前消息時,這個控制器可能改變用戶界面或者調(diào)整視圖位置,需要部分視圖重新繪制自己。當這個發(fā)生時,視圖和繪圖基礎組件接管并以可能的最有效率的方式來處理這些必要的重繪事件。
基本設計模式
UIKit框架的設計融合了許多Mac OS X上的Cocoa應用程序創(chuàng)建的設計模式。理解這些模式對于創(chuàng)建iPhone應用程序是關(guān)鍵的,所以值得花一些時間來了解它們。下面提供了一個這些設計模式的總體描述:
【編輯推薦】