iOS/iPhone 程式檔案目錄結構以及啟動流程
應用沙盒包含多個目錄:
1.應用程式包(application bundle):包含所有的資原始檔和可執行檔案,並且是隻讀檔案目錄。
注意:每個應用程式可以有多個程式包,但是隻能有一個主程式包,即包含應用程式程式碼的程式包。當用戶啟動應用程式的時候,應用程式會在主程式包中尋找即刻要用到的程式碼和資源,並將其載入到記憶體。然後它可以根據需要動態(並延時)載入主程式包或者叢屬程式包中的程式碼和資源。應用程式可以用NSBundle 和Core Foundation 的CFBundleRef(在過程化的語言中使用)定位程式包中的資源。在Objective-C中,首先要獲得一個NSBundle例項,它和某個物理程式包對應。如需獲得應用程式主程式包,則應呼叫mainBundle類方法。然後呼叫NSBundle的其他方法,傳入檔名、副檔名以及(可選)程式包子目錄,這些方法將會返回程式包資源的路徑。有了資源路徑,您就可以使用恰當的類將其載入到記憶體。程式碼案例有:
- NSString *str=[[NSBundle mainBundle]pathForResource:@"example" ofType:@"png"];
2.偏好設定檔案(Library/Preferences/):存放所有的偏好設定。通過NSUserDefaults類可以讀取寫入該檔案,同時可以作為應用的預設啟動設定,即應用的setting會在該目錄中查詢應用的設定資訊。
3.臨時檔案(tmp/):用於儲存程式執行時所需的臨時資料,使用完畢後的臨時資料對應的檔案將從該目錄下刪除。同時,應用結束時也可能會清除該目錄下的檔案。在程式執行時可以通過方法NSTemporaryDirectory可以的得到該沙盒下tmp目錄的全路徑。
4.應用執行時保留的資料(Documents/):儲存應用執行時生成的需要保留的資料。該檔案對於在裝置發生故障時,通過ituns同步裝置備份該目錄,從而可以恢復應用的資料。
5.儲存應用執行時生成的需要保留的資料(Library/Caches):與4的不同之處時ituns同步不能備份該目錄。因為該快取資料的體積比較大,會延長同步裝置所需的時間。但是如果資料來源在別處(例如,網路的伺服器),那麼可以通過將資料儲存在該目錄。當用戶需要恢復裝置,可以從伺服器下載這些資料。
可以總結一下,Library/Preferences/、Documents/ 下的檔案時可以通過iTuns同步裝置時進行備份目錄,而tmp/、Documents/是不能備份的。用表格表示如下:
目錄 |
描述 |
---|---|
<Application_Home> |
這是程式包目錄,包含應用程式的本身。由於應用程式必須經過簽名,所以您在執行時不能對這個目錄中的內容進行修改,否則可能會使應用程式無法啟動。 在iPhone OS 2.1及更高版本的系統,iTunes不對這個目錄的內容進行備份。但是,iTunes會對在App Store上購買的應用程式進行一次初始的同步。 |
<Application_Home> |
您應該將所有的應用程式資料檔案寫入到這個目錄下。這個目錄用於儲存使用者資料或其它應該定期備份的資訊。有關如何取得這個目錄路徑的資訊,請參見部分。 iTunes會備份這個目錄的內容。 |
<Application_Home> |
這個目錄包含應用程式的偏好設定檔案。您不應該直接建立偏好設定檔案,而是應該使用類或CFPreferences API來取得和設定應用程式的偏好,詳情請參見部分。 iTunes會備份這個目錄的內容。 |
<Application_Home> |
這個目錄用於存放應用程式專用的支援檔案,儲存應用程式再次啟動過程中需要的資訊。您的應用程式通常需要負責新增和刪除這些檔案,但在對裝置進行完全恢復的過程中,iTunes會刪除這些檔案,因此,您應該能夠在必要時重新建立。您可以使用 部分描述的介面來獲取該目錄的路徑,並對其進行訪問。 在iPhone OS 2.2及更高版本,iTunes不對這個目錄的內容進行備份。 |
<Application_Home> |
這個目錄用於存放臨時檔案,儲存應用程式再次啟動過程中不需要的資訊。當您的應用程式不再需要這些臨時檔案時,應該將其從這個目錄中刪除(系統也可能在應用程式不執行的時候清理留在這個目錄下的檔案)。有關如何獲得這個目錄路徑的資訊,請參見部分。 在iPhone OS 2.1及更高版本,iTunes不對這個目錄的內容進行備份。 |
上面介紹了沙盒的檔案系統結構,下面繼續講應用程式包中檔案,這裡面的資原始檔以及可執行檔案是程式工程開發時產生的。它基本上包含了:
1. .pch:預編譯標頭檔案,win32裡經常會碰到,這裡也有,包含了常用的標頭檔案。 2. .plist:包含了專案自身的特性,比如說專案名稱,預設載入的nib file,版本等。 3..xib:程式的資原始檔。用於簡化編碼過程,提高開發效率。 4. main.m:iOS應用程式的入口,類似於C/C++中的main函式。 到了這裡,基本就可以開始應用程式具體的啟動過程了。 在main.m檔案中,- <span style="font-size:14px;">int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- <strong>int retVal = UIApplicationMain(argc, argv, nil, nil);</strong>
- [pool release];
- return retVal;
- }
- </span>
main函式仍然是程式的入口函式,其中argc、argv為執行程式的支援命令列的引數輸入,而後面的兩個引數最後再講。之後可以看到重要的黑體程式行——UIApplicationMain 的呼叫,通過它完成系統啟動的過程,並形成一個事件驅動。那麼是如何完成系統的啟動過程呢?可以參照Apple提供的啟動流程圖:
可以看到UIApplication首先會去檢視info.plist中記錄的一些應用的基本資訊(如下圖),其中最重要的是應用程式啟動資原始檔的名稱(nib檔案,名稱用Main nib File base name鍵指定),如何建立的是universe的應用,那麼還會包含ipad的啟動資原始檔的名稱。從下圖中我們還看到了應用圖示檔案以及支援方向感應器的方向。
從工程檔案中找到Main nib file對應的MainWindow_iPhone.xib檔案,可以看到有四個基本的專案:
1.File’s Owner 物件:實際上就是 UIApplication 的例項。
2.First Responder 物件:每個程式都會有一個第一響應者,比如滑鼠事件,鍵盤事件等,它就是對應的那個物件。比如多文件程式中,menu的響應事件一般都是連線到FirstResponder中去的,因為主介面一般都在別的nib裡面,此時的FirstResponder就是你的那個主nib的FileOwner。
3.Delegate 物件:每個程式都有一個工程名+AppDelegate類,即為該Delegate的實現。
4.Window:應用程式啟動的時候所顯示的視窗。
程式啟動時,那麼就會發送訊息給UIApplication的Delegate物件,就會呼叫AppDelegate類的applicationDidFinishLaunching: 方法。因此可以在MainWindow_iPhone.xib指定某個特定的Delegate物件,從而啟動的位置會發生改變。
而在Delegate類中的applicationDidFinishLaunching:會初始化window物件,即程式啟動時候的顯示視窗。
具體的程式碼為:
- <span style="font-size:14px;">- (void)applicationDidFinishLaunching:(UIApplication *)application {
- // Override point for customization after app launch
- [window addSubview:UIViewControllerInstance.view];
- [window makeKeyAndVisible];
- }
- </span>
綜上所述,程式啟動的流程為:
講到這裡,如果沒有版本的更新問題,其實是可以結束了。而在XCode4.2版本之後,可能沒有了Main.xib來載入初始介面。那麼就不得不使用UIApplicationMain指定入口。前面講到了UIApplicationMain含有四個引數,這裡可以將UIApplicationMain(argc, argv, nil, nil);變成UIApplicationMain(argc, argv, nil,NSStringFromClass([AppDelegate class]));。這裡的AppDelegate為你要完成初始化介面的delegate。
除了上面的改變,版本更新還添加了故事板(storyBoard),那麼上面說的程式流程就不使用,請記住。
PS:對於iPhone app,你無法訪問iPhone整個的檔案系統,而只能訪問iPhone app的Home目錄中的檔案。在程式中可以用下面的方式來訪問檔案目錄:
方法1:
- NSString* documentsDirectory = [NSHomeDirectory()
- stringByAppendingPathComponent:@"Documents"];
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
- NSString *documentsDirectory = [paths objectAtIndex:0];
常量 |
目錄 |
---|---|
<Application_Home> |
|
<Application_Home> |