iOS應用程序一般都是由自己編寫的代碼和系統框架(system frameworks)組成,系統框架提供一些基本infrastructure給所有App來運行,而你提供自己編寫的代碼來定制App的外觀和行為。因此,了解iOS Infrastructure和它們如何工作對編寫App是很有幫助的。
所有基於C編寫的App的入口都是main函數,但iOS應用程序有點不同。不同就是你不需要為iOS應用程序而自己編寫main函數,當你使用Xcode創建工程的時候就已經提供了。除非一些特殊情況,否則你不應該修改Xcode提供的main函數實現。示例代碼如下:
[cpp] view plaincopy上面實例代碼中有一個很重要的函數UIApplicationMain,它主要是創建App的幾個核心對象來處理以下過程:
iOS應用程序都遵循Model-View-Controller的架構,Model負責存儲數據和處理業務邏輯,View負責顯示數據和與用戶交互,Controller是兩者的中介,協調Model和View相互協作。它們的通訊規則如下:
1. Controller能夠訪問Model和View,Model和View不能互相訪問。
2. 當View與用戶交互產生事件時,使用target-action方式來處理。
3. 當View需要處理一些特殊UI邏輯或獲取數據源時,通過delegate或data source方式交給Controller來處理。
4. Model不能直接與Controller通信,當Model有數據更新時,可以通過Notification或KVO (Key Value Observing)來通知Controller更新View。
了解iOS的MVC設計模式之後,我們從下圖來了解在MVC模式下iOS應用程序有哪些關鍵對象以及它們職責主要是什麼?
用戶與iOS設備交互時產生的事件(Multitouch Events,Motion Event,Remote Control Event)交由UIApplication對象來分發給control objects(UIControl)對應的target objects來處理並且管理整個事件循環,而一些關於App運行時重要事件委托給app delegate來處理。
App delegate對象遵循UIApplicationDelegate協議,響應app運行時重要事件(app啟動、app內存不足、app終止、切換到另一個app、切回app),主要用於app在啟動時初始化一些重要數據結構;例如,初始化UIWindow,設置一些屬性,為window添加rootViewController。
View Controller有一個view屬性是view層次結構中的根view,你可以添加子view來構建復雜的view;controller有一些viewDidLoad、viewWillAppear等方法來管理view的生命周期;由於它繼承UIResponder,所有還會響應和處理用戶事件。
data model對象主要用來存儲數據。例如,餓了麼app在搜索切換地址後,有歷史記錄搜索地址歷史,當app下次啟動時,讀取和顯示搜索地址歷史。
document對象(繼承UIDocument)用來管理一些或所有的data model對象。document對象並不是必須的,但提供一種方便的方式來分組屬於單個文件或多個文件的數據。
UIWindow對象位於view層次結構中的最頂層,它充當一個基本容器而不顯示內容,如果想顯示內容,添加一個content view到window。
它也是繼承UIResponder,所以它也是會響應和處理用戶事件。
View對象可以通過addSubview和removeFromSuperview 等方法管理view的層次結構,使用layoutSubviews、layoutIfNeeded和setNeedsLayout等方法布局view的層次結構,當你發現系統提供view已經滿足不了你想要的外觀需求時,可以重寫drawRect方法或通過layer屬性來構造復雜的圖形外觀和動畫。還有一點,UIView也是繼承UIResponder,所以也能夠處理用戶事件。
Control對象通常就是處理特定類型用戶交互的View,常用的有button、switch、text field等。
除了使用View和Control來構建view層次結構來影響app外觀之外,還可以使用Core Animation框架的Layer對象來渲染view外觀和構建復雜的動畫。
一個iOS應用程序的main run loop主要作用是處理所有與用戶相關的事件。UIApplication對象在啟動時就設置main run loop和使用它來處理事件和更新基於view的界面。正如它的名字顯示,main run loop是運行在應用程序的主線程。這樣就確保與接收到用戶相關的事件被有序地處理。
下圖顯示main run loop的架構和用戶事件最終是怎樣被應用程序處理。當用戶與設備交互時,系統就會生成與交互關聯的事件,然後被應用程序的UIKit通過一個特殊的端口來分發。應用程序把事件放入隊列,然後逐個分發到main run loop來執行。UIApplication對象是第一個對象接收到事件,然後決定怎樣處理它。一個touch event通常都被分發到main window對象,然後依次分發到發生觸碰的view。其他event的接收事件對象路徑可能有點不同。
大多數的事件通過使用main run loop來分發,但有些不是。有些事件被發送到一個delegate對象或傳遞到你提供的block中。想了解更多如何處理大多數類型的事件,其中包括touch、remote control、motion、accelerometer和gyroscopic等事件,請查閱Event Handle Guide for iOS。
有時系統會從App一種狀態切換另一種狀態來響應系統發生的事件。例如,當用戶按下Home鍵、電話打入或其他中斷發生時,當前運行的應用程序會切換狀態來響應。應用程序的狀態有以下幾種:
大多數發生狀態轉換時都會調用delegate對象對應的方法來響應App的狀態改變。下面匯總了delegate對象的所有方法,當App狀態發生轉換時,你可能會使用到它們。
現在講下App啟動、來回切換App和鎖屏時狀態的切換和調用對應哪些delegate對象的方法:
如圖所示,當App啟動時,首先由not running狀態切換到inactive狀態,此時調用application:didFinishLaunchingWithOptions:方法;然後由inactive狀態切換到active狀態,此時調用applicationDidBecomeActive:方法。
當App發生中斷時,由active狀態切換到inactive狀態,此時調用applicationWillResignActive:方法。
如圖所示,當切換到另一個App時,由狀態active切換到inactive,此時調用applicationWillResignActive:方法;然後從inactive狀態切換到running狀態,此時調用applicationDidEnterBackground:方法。
而當切換回本來的App時,由running狀態切換到inactive狀態,此時調用applicationWillEnterForeground:方法,然後由inactive狀態切換到active狀態,調用applicationDidBecomeActive:方法。
如何所示,當手機鎖屏時,由狀態active切換到inactive,此時調用applicationWillResignActive:;然後再由inactive狀態切換到running狀態,此時調用applicationDidEnterBackground:方法。
更多關於app狀態切換以及調用app delegate哪些方法,請觀看WWDC 2011 Session的session_320__adopting_multitasking_in_your_app視頻。
系統常常是為其他app啟動時由於內存不足而回收內存最後需要終止應用程序,但有時也會是由於app很長時間才響應而終止。如果app當時運行在後台並且沒有暫停,系統會在應用程序終止之前調用applicationWillTerminate:來保存用戶的一些重要數據以便下次啟動時恢復到app原來的狀態。
本文總結了iOS應用程序從啟動到結束過程中有哪些關鍵對象在參與,以及當用戶與系統交互時產生事件時,系統利用main run loop來管理事件循環,決定將事件交給系統哪些對象處理和如何處理。而當App啟動、來回切換App和鎖屏時,App的狀態如何切換和調用對應的哪些app delegate對象來處理。
作者簡介:劉耀柱(@Sam_Lau_Dev),iOS Developer兼業余Designer,參與開發技術前線iOS項目翻譯,個人博客:http://www.jianshu.com/users/256fb15baf75/latest_articles,GitHub:https://github.com/samlaudev。