聊一聊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/