天天看點

iOS開發之旅之App的生命周期

在iOS App中,入口函數并不在根目錄下,而是在“Supporting Files”目錄的main.m檔案的main函數中。這很容易了解,C/C++都是以main為入口。

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

這個函數比較簡單,隻是調用了UIApplicationMain方法來啟動整個應用程式,前兩個參數就是普通C/C++的指令行參數,這裡我們可以忽略。主要看後面兩個參數。後兩個參數分别表示程式的主要類(principal class)和app代理類(delegate class)。如果主要類(principal class)為nil,将從Info.plist中擷取,如果Info.plist中不存在對應的key,則預設為UIApplication;App代理類(delegate class)将在建立工程時建立,即AppDelegate,應用程式的整個生命周期都由它來代理。

APP生命周期

       根據UIApplicationMain函數,程式将進入AppDelegate.m,這個檔案是xcode建立工程時自動生成的。下面看一下AppDelegate.m檔案,這個關乎着應用程式的生命周期。

#import "AppDelegate.h"
@interface AppDelegate ()
@end

@implementation AppDelegate
// 應用程式第一次啟動時執行該函數,如果是手寫代碼設定應用程式window的rootViewController那麼則需要在這裡實作。該函數的功能等同于Android中的onCreate函數。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}
// 應用程式由激活狀态切換到未激活狀态要執行的函數,例如使用者按home鍵傳回主螢幕等。類似于Android中的onPause回調函數
- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
// 應用程式已進入背景程式時的回調函數,類似于Android中的onStop
- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
// 應用程式從未激活狀态進入到激活狀态要執行的回調函數,過程與WillResignActive相反,等同于Android中的onRestart函數。
- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
// 應用程式被激活的回調,與didEnterBackground過程想對應。onResume
- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
// 應用程式即将終止的回調函數
- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
           
  • application:willFinishLaunchingWithOptions:

    —這方法在應用第一次啟動時執行,即隻執行一次。類似于Android中的onCreate函數。
  • application:didFinishLaunchingWithOptions:

    —這個函數允許你在應用顯示在使用者面前之前進行最後的初始化工作。
  • applicationDidBecomeActive:

    —在應用程式成為前台程式時要執行的回調函數,類似于Android中的onResume函數。
  • applicationWillResignActive:

    —在應用程式從前台轉換到背景程式時會調用的函數,類似于Android中的onStop函數。
  • applicationDidEnterBackground:

    —應用程式進入到背景狀态的回調函數,此時的應用程式可能在任何時刻被挂起。
  • applicationWillEnterForeground:

    —應用程式從背景進入到前台的回調函數,但此時應用程式還不是激活狀态,類似于Android中的onRestart函數。
  • applicationWillTerminate:

    —應用程式将要被終止時的回調函數,如果你的程式隻是被挂起,那麼不會回調該函數,類似于Android中的onDestory函數。
iOS開發之旅之App的生命周期
iOS開發之旅之App的生命周期

圖1 圖2

如上圖1所示,應用程式啟動UIApplication,此時主線程( UI線程 )的事件循環就會開啟。并且會将App的生命周期代理給AppDelegate,首先會調用

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ,我們又會在該函數中設定AppDelegate中window的rootViewController為我們的第一個頁面(更多的情況是系統自動加載應用程式的預設的storyboard界面),我們會在自己的ViewController中設計UI,是以應用程式window中就加載了我們的UI,此時應用程式就從最初的not running狀态到了Active狀态。

UI的線程安全

在上文中我們提到了應用程式啟動時會啟動一個消息循環,并且這個消息循環是在主線程中的。開發過Android應用程式的同學都知道,更新UI必須在主線程中,這是因為UI控件并不是線程安全的,在Android中UI控件都是ThreadLocal的,這個ThreadLocal就是就是主線程的ThreadLocal,可以簡單的了解為UI控件的操作隻能在主線程中執行才是安全的,如果在其他線程中操作就會抛出異常,這就是UI控件的非線程安全性。

iOS中的UI控件也不是線程安全的。因為App中UI更新頻率時是很高的,如果UI是線程安全的,也就是UI控件在子線程中可以修改、更新等,那麼系統必須要對各種UI操作進行鎖操作,加鎖、解鎖、系統排程等會消耗大量的CPU資源,這樣就會導緻效率底下,而且容易導緻死鎖問題。是以,UI操作隻能在主線程中。官方解釋如下 :

Threading ConsiderationsManipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

UIWindow與UIView

UIWindow就是應用程式視窗,簡單了解就是整個手機螢幕。UIWindow的主要功能就是提供一個區域來顯示UI視圖和将事件分發給視圖。在應用加載時,我們會設定或者由系統從plist檔案中加載UIWindow的rootViewController,在UIViewController中又包含了各種UI控件,當應用啟動時,啟動了消息循環,回調App的生命周期函數,将UI控件繪制到UIWindow中,然後又通過UIWindow将使用者的各種操作通過事件的形式分發給UI控件,至此整個App就運轉起來了。

參考文檔 : https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html#//apple_ref/doc/uid/TP40007072-CH2-SW1

繼續閱讀