07iOS應用程序的生命周期

0. 引子

iOS應用程序一般都是由自己編寫的代碼系統框架(system frameworks)組成,系統框架提供一些基本infrastructure給所有app來運行,而你提供自己編寫的代碼來定制app的外觀和行為。因此,了解iOS infrastructure和它們如何工作對編寫app是很有幫助的。

1. Main函數入口

所有基于C編寫的app的入口都是main
函數,但iOS應用程序有點不同。不同就是你不需要為iOS應用程序而自己編寫main
函數,當你使用Xcode創建工程的時候就已經提供了。除非一些特殊情況,否則你不應該修改Xcode提供的main
函數實現。示例代碼如下:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[])
{ 
     @autoreleasepool { 
           return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
     } 
}

上面實例代碼中有一個很重要的函數UIApplicationMain,它主要是創建app的幾個核心對象來處理以下過程:

  • 1.從可用Storyboard文件加載用戶界面
  • 2.調用AppDelegate自定義代碼來做一些初始化設置
  • 3.將app放入Main Run Loop環境中來響應和處理與用戶交互產生的事件

2.應用程序的架構

2.1 MVC

iOS應用程序都遵循Model-View-Controller
的架構,Model負責存儲數據和處理業務邏輯,View負責顯示數據和與用戶交互,Controller是兩者的中介,協調Model和View
相互協作。它們的通訊規則如下:

  • 1.Controller能夠訪問Model和View,Model和View不能互相訪問
MVC
  • 2.當View與用戶交互產生事件時,使用target-action方式來處理
Stanford University
  • 3.當View需要處理一些特殊UI邏輯或獲取數據源時,通過delegate或data source方式交給Controller來處理
View
  • 4.Model不能直接與Controller通信,當Model有數據更新時,可以通過Notification或KVO (Key Value Observing)來通知Controller更新View
Model

2.2 幾個關鍵對象

了解iOS的MVC設計模式之后,我們從下圖來了解在MVC模式下iOS應用程序有哪些關鍵對象以及它們職責主要是什么?

The Structure of an App
  • 1.UIApplication對象
    用戶與iOS設備交互時產生的事件(Multitouch Events,Motion Event,Remote Control Event)交由UIApplication
    對象來分發給control objects(UIControl)對應的target objects來處理并且管理整個事件循環,而一些關于app運行時重要事件委托給app delegate
    來處理。

  • 2.App delegate對象
    App delegate對象遵循UIApplicationDelegate
    協議,響應app運行時重要事件(app啟動、app內存不足、app終止、切換到另一個app、切回app),主要用于app在啟動時初始化一些重要數據結構;例如,初始化UIWindow,設置一些屬性,為window添加rootViewController。

  • 3.View controller對象
    View Controller有一個view屬性是view層次結構中的根view,你可以添加子view來構建復雜的view;controller有一些viewDidLoad、viewWillAppear等方法來管理view的生命周期;由于它繼承UIResponder,所有還會響應和處理用戶事件。

  • 4.Documents和data model對象
    data model對象主要用來存儲數據。例如,餓了么app在搜索切換地址后,有歷史記錄搜索地址歷史,當app下次啟動時,讀取和顯示搜索地址歷史。document對象(繼承UIDocument)用來管理一些或所有的data model對象。document對象并不是必須的,但提供一種方便的方式來分組屬于單個文件或多個文件的數據。

  • 5.UIWindow對象UIWindow
    對象位于view層次結構中的最頂層,它充當一個基本容器而不顯示內容,如果想顯示內容,添加一個content view到window。它也是繼承UIResponder
    ,所以它也是會響應和處理用戶事件。

  • 6.Viewcontrollayer對象
    View對象可以通過addSubview和removeFromSuperview 等方法管理view的層次結構,使用layoutIfNeeded和setNeedsLayout等方法布局view的層次結構,當你發現系統提供view已經滿足不了你想要的外觀需求時,可以重寫drawRect方法或通過layer屬性來構造復雜的圖形外觀和動畫。還有一點,UIView也是繼承UIResponder,所以也能夠處理用戶事件
    Control對象通常就是處理特定類型用戶交互的View,常用的有button、switch、text field等。
    除了使用View和Control來構建view層次結構來影響app外觀之外,還可以使用Core Animation框架的Layer對象來渲染view外觀和構建復雜的動畫。

3. Main Run Loop

一個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

4. 應用程序的狀態和多任務

4.1

有時系統會從app一種狀態切換另一種狀態來響應系統發生的事件。例如,當用戶按下home鍵、電話打入、或其他中斷發生時,當前運行的應用程序會切換狀態來響應。應用程序的狀態有以下幾種:

App狀態切換
  • Not running:app還沒運行
  • Inactive:app運行在foreground但沒有接收事件
  • Active:app運行在foreground和正在接收事件
  • Background:運行在background和正在執行代碼
  • Suspended:運行在background但沒有執行代碼

4.2

大多數發生狀態轉換時都會調用delegate對象對應的方法來響應app的狀態改變。下面匯總了delegate對象的所有方法,當app狀態發生轉換時,你可能會使用到它們。

  • application:willFinishLaunchingWithOptions: - 這個方法是你在啟動時的第一次機會來執行代碼
  • application:didFinishLaunchingWithOptions: - 這個方法允許你在顯示app給用戶之前執行最后的初始化操作
  • applicationDidBecomeActive: - app已經切換到active狀態后需要執行的操作
  • applicationWillResignActive: - app將要從前臺切換到后臺時需要執行的操作
  • applicationDidEnterBackground: - app已經進入后臺后需要執行的操作
  • applicationWillEnterForeground: - app將要從后臺切換到前臺需要執行的操作,但app還不是active狀態
  • applicationWillTerminate: - app將要結束時需要執行的操作

4.3

現在講下app啟動、來回切換app和鎖屏時狀態的切換和調用對應哪些delegate對象的方法:

  • app啟動和active/inactive
    圖1

如圖所示,當app啟動時,首先由not running狀態切換到inactive狀態,此時調用application:didFinishLaunchingWithOptions:方法;然后由inactive狀態切換到active狀態,此時調用applicationDidBecomeActive:方法。

圖2

當app發生中斷時,由active狀態切換到inactive狀態,此時調用applicationWillResignActive:方法。

  • 來回切換app
    圖3

如圖所示,當切換到另一個app時,由狀態active切換到inactive,此時調用applicationWillResignActive:方法;然后從inactive狀態切換到running狀態,此時調用applicationDidEnterBackground:方法。

圖4

而當切換回本來的app時,由running狀態切換到inactive狀態,此時調用applicationWillEnterForeground:方法,然后由inactive狀態切換到active狀態,調用applicationDidBecomeActive:方法。

  • 鎖屏
    圖5

如何所示,當手機鎖屏時,由狀態active
切換到inactive
,此時調用applicationWillResignActive:
;然后再由inactive
狀態切換到running
狀態,此時調用applicationDidEnterBackground:
方法。
更多關于app狀態切換以及調用app delegate
哪些方法,請觀看WWDC 2011 Session的session_320__adopting_multitasking_in_your_app視頻。

5. 應用程序的終止

系統常常是為其他app啟動時由于內存不足而回收內存最后需要終止應用程序,但有時也會是由于app很長時間才響應而終止。如果app當時運行在后臺并且沒有暫停,系統會在應用程序終止之前調用applicationWillTerminate:來保存用戶的一些重要數據以便下次啟動時恢復到app原來的狀態。

6. 總結

本文總結了iOS應用程序從啟動到結束過程中有哪些關鍵對象在參與,以及當用戶與系統交互時產生事件時,系統利用main run loop來管理事件循環,決定將事件交給系統哪些對象處理和如何處理。而當app啟動、來回切換app和鎖屏時,app的狀態如何切換和調用對應的哪些app delegate對象來處理。

7. 擴展閱讀

App Programming Guide for iOS
Developing iOS 7 App for iPhone and iPad
深入理解RunLoop
Objective-C Autorelease Pool 的實現原理

20180322iOS應用生命周期總結

應用程序的狀態:

  • Not running未運行:程序沒啟動。

  • Inactive未激活:程序在前臺運行,不過沒有接收到事件。在沒有事件處理情況下程序通常停留在這個狀態。

  • Active激活:程序在前臺運行而且接收到了事件。這也是前臺的一個正常的模式。

  • Backgroud后臺:程序在后臺而且能執行代碼,大多數程序進入這個狀態后會在在這個狀態上停留一會。時間到之后會進入掛起狀態(Suspended)。有的程序經過特殊的請求后可以長期處于Backgroud狀態。

  • Suspended掛起:程序在后臺不能執行代碼。系統會自動把程序變成這個狀態而且不會發出通知。當掛起時,程序還是停留在內存中的,當系統內存低時,系統就把掛起的程序清除掉,為前臺程序提供更多的內存。

iOS的入口在main.m文件

int main(int argc, char *argv[])
{
      @autoreleasepool {
                 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
      }
 }

main函數的兩個參數,iOS中沒有用到,包括這兩個參數是為了與標準ANSI C保持一致。 UIApplicationMain函數,前兩個和main函數一樣,重點是后兩個。

后兩個參數分別表示程序的主要類(principal class)和代理類(delegate class)。如果主要類(principal class)為nil,將從Info.plist中獲取,如果Info.plist中不存在對應的key,則默認為UIApplication;如果代理類(delegate class)將在新建工程時創建。

根據UIApplicationMain函數,程序將進入AppDelegate.m,這個文件是xcode新建工程時自動生成的。下面看一下AppDelegate.m文件,這個關乎著應用程序的生命周期。

1、application didFinishLaunchingWithOptions:當應用程序啟動時執行,應用程序啟動入口,只在應用程序啟動時執行一次。若用戶直接啟動,lauchOptions內無數據,若通過其他方式啟動應用,lauchOptions包含對應方式的內容。

2、applicationWillResignActive:在應用程序將要由活動狀態切換到非活動狀態時候,要執行的委托調用,如 按下 home 按鈕,返回主屏幕,或全屏之間切換應用程序等。

3、applicationDidEnterBackground:在應用程序已進入后臺程序時,要執行的委托調用。

4、applicationWillEnterForeground:在應用程序將要進入前臺時(被激活),要執行的委托調用,剛好與applicationWillResignActive 方法相對應。

5、applicationDidBecomeActive:在應用程序已被激活后,要執行的委托調用,剛好與applicationDidEnterBackground 方法相對應。

6、applicationWillTerminate:在應用程序要完全推出的時候,要執行的委托調用,這個需要要設置UIApplicationExitsOnSuspend的鍵值。

初次啟動:

iOS_didFinishLaunchingWithOptions
iOS_applicationDidBecomeActive

按下home鍵:

iOS_applicationWillResignActive
iOS_applicationDidEnterBackground

點擊程序圖標進入:

iOS_applicationWillEnterForeground
iOS_applicationDidBecomeActive

當應用程序進入后臺時,應該保存用戶數據或狀態信息,所有沒寫到磁盤的文件或信息,在進入后臺時,最后都寫到磁盤去,因為程序可能在后臺被殺死。釋放盡可能釋放的內存。

 - (void)applicationDidEnterBackground:(UIApplication *)application{
 }

方法有大概5秒的時間讓你完成這些任務。如果超過時間還有未完成的任務,你的程序就會被終止而且從內存中清除。

如果還需要長時間的運行任務,可以在該方法中調用:

[application beginBackgroundTaskWithExpirationHandler:^{ 
      NSLog(@"begin Background Task With Expiration Handler"); 
 }];

程序終止

程序只要符合以下情況之一,只要進入后臺或掛起狀態就會終止:

①iOS 4.0以前的系統

②app是基于iOS 4.0之前系統開發的。

③設備不支持多任務

④在Info.plist文件中,程序包含了 UIApplicationExitsOnSuspend 鍵。

系統常常是為其他app啟動時由于內存不足而回收內存最后需要終止應用程序,但有時也會是由于app很長時間才響應而終止。如果app當時運行在后臺并且沒有暫停,系統會在應用程序終止之前調用app的代理的方法 - (void)applicationWillTerminate:(UIApplication *)application,這樣可以讓你可以做一些清理工作。你可以保存一些數據或app的狀態。這個方法也有5秒鐘的限制。超時后方法會返回程序從內存中清除。

注意:用戶可以手工關閉應用程序

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容