聊一聊iOS的那些生命周期

HasHLI 7年前發布 | 10K 次閱讀 程序員 iOS開發 移動開發

iOS應用程序的生命周期,還有程序是運行在前臺還是后臺,應用程序各個狀態的變換,這些對于開發者來說都是很重要的。iOS系統的資源是有限的,應用程序在前臺和在后臺的狀態是不一樣的。在后臺時,程序會受到系統的很多限制,這樣可以提高電池的使用和用戶體驗。

本文所要說到的生命周期,也不僅僅只是應用生命周期;還包括,視圖生命周期。

應用生命周期

iOS的應用程序一共有5種狀態:

  • Not Running(非運行狀態)

應用沒有運行或被系統終止。

  • Inactive(前臺非活動狀態)

應用正在進入前臺狀態,但是還不能接受事件處理。

  • Active(前臺活動狀態)

應用進入前臺狀態,能接受事件處理。

  • Background(后臺狀態)

應用進入后臺后,依然能夠執行代碼。如果有可執行的代碼,就會執行代碼,如果沒有可執行的代碼或者將可執行的代碼執行完畢,應用會馬上進入掛起狀態。有的程序經過特殊的請求后可以長期處于Backgroud狀態。

  • Suspended(掛起狀態)

處于掛起的應用進入一種“冷凍”狀態,不能執行代碼。如果系統內存不夠,系統就把掛起的程序清除掉,為前臺程序提供更多的內存,應用會被終止。

作為應用程序的委托對象,AppDelegate類在應用生命周期的不同階段會回調不同的方法。首先,讓我們先了解一下iOS 應用的不同狀態及它們彼此間的關系,如下圖所示 :

在應用狀態躍遷的過程中,iOS 系統會回調AppDelegate中的一些方法,并且發送一些通知。實際上,在應用的生命周期中用到的方法和通知很多,我們選取了幾個主要的方法和通知進行詳細介紹,具體如下表所述:

為了便于觀察應用程序的運行狀態,為AppDelegate.m中的方法添加一些日志輸出,具體代碼如下:

@implementation AppDelegate 

- (BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    NSLog(@"%@", @"application:didFinishLaunchingWithOptions:");

    return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application{    
    NSLog(@"%@", @"applicationWillResignActive:");
}
- (void)applicationDidEnterBackground:(UIApplication *)application{    
    NSLog(@"%@", @"applicationDidEnterBackground:");
}
- (void)applicationWillEnterForeground:(UIApplication *)application{    
    NSLog(@"%@", @"applicationWillEnterForeground:");
}
- (void)applicationDidBecomeActive:(UIApplication *)application{    
    NSLog(@"%@", @"applicationDidBecomeActive:");
}
- (void)applicationWillTerminate:(UIApplication *)application{    
    NSLog(@"%@", @"applicationWillTerminate:");
}
@end

為了讓大家更直觀地了解各狀態與其相應的方法、通知間的關系,下面以幾個應用場景為切入點進行系統的分析。

(一)非運行狀態——應用啟動場景

場景描述:用戶點擊應用圖標的時候,可能是第一次啟動這個應用,也可能是應用終止后再次啟動。該場景的狀態躍遷過程見下圖,共經歷兩個階段3個狀態:Not running →Inactive→Active。

  • 1)在Not running→Inactive 階段。

調用 application:didFinishLaunchingWithOptions: 方法,發出 UIApplicationDidFinishLaunchingNotification 通知。

  • 2)在Inactive →Active 階段。

調用 applicationDidBecomeActive: 方法,發出 UIApplicationDidBecomeActiveNotification 通知。

(二)點擊Home鍵——應用退出場景

場景描述:應用處于運行狀態(即Active狀態)時,點擊Home鍵或者有其他的應用導致當前應用中斷。該場景的狀態躍遷過程可以分成兩種情況:可以在后臺運行或者掛起,不可以在后臺運行或者掛起。根據產品屬性文件(如HelloWorld-Info.plist)中的相關屬性Application does not run in background 是與否可以控制這兩種狀態。如果采用文本編輯器打開HelloWorldInfo.plist文件該設置項對應的鍵是UIApplicationExitsOnSuspend。

狀態躍遷的第一種情況:應用可以在后臺運行或者掛起,該場景的狀態躍遷過程見下圖 ,共經歷3 個階段4個狀態:Active → Inactive → Background→Suspended。

  • 1)在Active→Inactive 階段。

調用 applicationWillResignActive: 方法,發出 UIApplicationWillResignActiveNotification 通知。

  • 2)在Inactive →Background階段。

應用從非活動狀態進入到后臺(不涉及我們要重點說明的方法和通知)。

  • 3)在Background→Suspended 階段。

調用 applicationDidEnterBackground: 方法,發出 UIApplicationDidEnterBackgroundNotification 通知。

狀態躍遷的第二種情況:應用不可以在后臺運行或者掛起,其狀態躍遷情況見下圖 ,共經歷4個階段5 個狀態:Active → Inactive → Background→Suspended→Not running 。

  • 1)在Active →Inactivd 階段。

應用由活動狀態轉為非活動狀態(不涉及我們要重點說明的方法和通知)。

  • 2)在Inactive →Background階段。

應用從非活動狀態進入到后臺(不涉及我們要重點說明的方法和通知)。

  • 3)在Background→Suspended 階段。

調用 applicationDidEnterBackground: 方法, 發出 UIApplicationDidEnterBackgroundNotification 通知。

  • 4)在Suspended →Not running階段。

調用 applicationWillTerminate: 方法,發出 UIApplicationWillTerminateNotification 通知。

iOS 在iOS 4之前不支持多任務,點擊Home鍵時,應用會退出并中斷;而在iOS 4之后(包括iOS 4),操作系統能夠支持多任務處理,點擊Home鍵應用會進入后臺但不會中斷(內存不夠的情況除外)。

應用在后臺也可以進行部分處理工作,處理完成則進入掛起狀態。

(三)掛起重新運行場景

場景描述:掛起狀態的應用重新運行。該場景的狀態躍遷過程如下圖所示,共經歷3 個階段4 個狀態:Suspended → Background → Inactive → Active 。

  • 1)Suspended→Background階段。

應用從掛起狀態進入后臺(不涉及我們講述的這幾個方法和通知)。

  • 2)Background→Inactive 階段。

調用 applicationWillEnterForeground: 方法,發出 UIApplicationWillEnterForegroundNotification 通知。

  • 3)Inactive →Active 階段。

調用 applicationDidBecomeActive: 方法,發出 UIApplicationDidBecomeActiveNotification 通知。

(四)內存清除——應用終止場景

場景描述:應用在后臺處理完成時進入掛起狀態(這是一種休眠狀態),如果這時發出低內存警告,為了滿足其他應用對內存的需要,該應用就會被清除內存從而終止運行,該場景的狀態躍遷見下圖 。

內存清除的時候應用終止運行。內存清除有兩種情況,可能是系統強制清除內存,也可能是由使用者從任務欄中手動清除(即刪掉應用)。內存清除后如果應用再次運行,上一次的運行狀態不會被保存,相當于應用第一次運行。

在內存清除場景下,應用不會調用任何方法,也不會發出任何通知。

視圖生命周期

視圖是應用的一個重要組成部分,功能的實現與其息息相關,而視圖控制器控制著視圖,其重要性在整個應用中不言而喻。

視圖生命周期與視圖控制器關系

以視圖的4 種狀態為基礎,我們來系統了解一下視圖控制器的生命周期。在視圖不同的生命周期中,視圖控制器會回調不同的方法,具體如下圖所示。

在視圖控制器已被實例化,視圖被加載到內存中時調用viewDidLoad方法,這個時候視圖并未出現。在該方法中,通常進行的是對所控制的視圖進行初始化處理。

視圖可見前后會調用 viewWillAppear: 方法和 viewDidAppear: 方法;視圖不可見前后會調用 viewWillDisappear: 方法和 viewDidDisappear: 方法。4個方法調用父類相應的方法以實現其功能,編碼時該方法的位置可根據實際情況做以調整,參見如下代碼:

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:YES];
    //something code
}

viewDidLoad 方法在應用運行的時候只調用一次,而這上述4 個方法可以被反復調用多次,它們的使用很廣泛但同時也具有很強的技巧性。例如,有的應用會使用重力加速計,重力加速計會不斷輪詢設備以實時獲得設備在z 軸、x 軸和y軸方向的重力加速度。不斷的輪詢必然會耗費大量電能進而影響電池使用壽命,我們通過利用這4個方法適時地打開或者關閉重力加速計來達到節約電能的目的。怎么使用這4 個方法才能做到“適時”是一個值得思考的問題。

在低內存情況下,iOS 會調用 didReceiveMemoryWarning: 和 viewDidUnload: 方法。在iOS 6 之后,就不再使用 viewDidUnload: ,而僅支持 didReceiveMemoryWarning: 。

didReceiveMemoryWarning: 方法的主要職能是釋放內存,包括視圖控制器中的一些成員變量和視圖的釋放。現舉例如下:

- (void)didReceiveMemoryWarning {
    self.button = nil;
    self.myStringD = nil; 
    [myStringC release];    //ARC內存管理情況下不用
    [super didReceiveMemoryWarning];
}

除了上述5 個方法視圖控制器外,還有很多其他方法。

iOS UI 狀態保持和恢復

iOS 設計規范中要求,當應用退出的時候(包括被終止運行的時候),需要保持界面中UI元素的狀態,當再次進來的時候看到的狀態與退出時是一樣的。在iOS 之后,蘋果提供以下API使得UI狀態保持和恢復變得很容易。

在iOS 中,我們可以在以下3種地方實現狀態保持和恢復:

  • 應用程序委托對象

  • 視圖控制器

  • 自定義視圖

恢復標識是iOS為了實現UI狀態保持和恢復添加的設置項目。我們還需要在應用程序委托對象AppDelegate代碼部分做一些修改,添加的代碼如下:

-(BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder{
    return YES;
}

-(BOOL) application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder{
    return YES;
}

- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder{
    [coder encodeFloat:2.0 forKey:@"Version"];
}

- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder{
    float lastVer = [coder decodeFloatForKey:@"Version"];
    NSLog(@"lastVer = %f",lastVer);
}

其中application:shouldSaveApplicationState:方法在應用退出時調用,負責控制是否允許保存狀態,返回YES 情況是可以保存,NO是不保存。

application:shouldRestoreApplicationState: 方法在應用啟動時調用,負責控制是否恢復上次退出時的狀態,返回YES 表示可以恢復,返回NO表示不可以恢復。

application:willEncodeRestorableStateWithCoder: 方法在保存時調用,在這個方法中實現UI狀態或數據的保存,其中 [coder encodeFloat:2.0 forKey:@"Version"] 語句是保存簡單數據。

application:didDecodeRestorableStateWithCoder: 方法在恢復時調用,在這個方法中實現UI狀態或數據的恢復,其中 [coder decodeFloatForKey:@"Version"] 語句用于恢復上次保存的數據。

想要實現具體界面中控件的保持和恢復,還需要在它的視圖控制器中添加一些代碼。我們在ViewController.m中添加的代碼如下:

-(void)encodeRestorableStateWithCoder:(NSCoder *)coder{
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeObject:self.txtField.text forKey:kSaveKey];
}

-(void)decodeRestorableStateWithCoder:(NSCoder *)coder{
    [super decodeRestorableStateWithCoder:coder];
    self.txtField.text = [coder decodeObjectForKey:kSaveKey];
}

在iOS 6之后,視圖控制器都添加了兩個方法—— encodeRestorableStateWithCoder: 和 decodeRestorableStateWithCoder: ,用來實現該控制器中的控件或數據的保存和恢復。

其中 encodeRestorableStateWithCoder: 方法在保存時候調用, [coder encodeObject:self. txtField.textforKey:kSaveKey] 語句是按照指定的鍵保存文本框的內容。

decodeRestorableStateWithCoder: 方法在恢復時調用, [coder decodeObjectForKey:kSaveKey] 在恢復文本框內容時調用,保存和恢復事實上就是向一個歸檔文件中編碼和解碼的過程。

移除Main.storyboard

每次使用Single View Application模板創建工程之后,總是會有一個Main.storyboard文件,那么,當我們使用代碼布局的時候,很顯然是不需要它的。那么,如何將它從工程中移除呢?只要進行如下幾步即可。

在工程配置中移除關聯

在TARGETS中,將Main InInterface選項中的值清空并保存設置。

移除Main.storyboard中的關聯文件

選擇storyboard文件。將類關聯文件項清空并保存設置。

移除Main.storyboard文件

從工程中移除文件。

在AppDelegate中添加代碼

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.   
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    ViewController *viewController = [[ViewController alloc] init];
    self.window.rootViewController = viewController;  
    self.window.backgroundColor = [UIColor purpleColor];
    [self.window makeKeyAndVisible];    
   return YES;
}

完成以上幾步,運行工程即可,順利運行,沒有出現任何error或waring。

 

來自:http://charsdavy.github.io/2017/04/11/ios-lifecycle/

 

 本文由用戶 HasHLI 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!