iOS開發(fā) - APP 的生命周期

I. 程序啟動(dòng)


1. 程序啟動(dòng)的完整過程:

  • 創(chuàng)建 UIApplication 對象
  • 創(chuàng)建 UIApplication 的 delegate 對象 delegate對象開始處理(監(jiān)聽)系統(tǒng)事件(沒有 storyboard)
  • 程序啟動(dòng)完畢的時(shí)候, 就會(huì)調(diào)用代理的application:didFinishLaunchingWithOptions:方法
  • application:didFinishLaunchingWithOptions:中創(chuàng)建UIWindow
  • 創(chuàng)建和設(shè)置 UIWindow 的 rootViewController
  • 顯示窗口 根據(jù)Info.plist獲得最主要storyboard的文件名,加載最主要的storyboard(有storyboard)
  • 創(chuàng)建UIWindow
  • 創(chuàng)建和設(shè)置UIWindow的rootViewController
  • 顯示窗口

2. UIApplicationMain:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

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

main函數(shù)中執(zhí)行了一個(gè)UIApplicationMain這個(gè)函數(shù):
intUIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);

argc、argv:

直接傳遞給UIApplicationMain進(jìn)行相關(guān)處理即可

principalClassName:

指定應(yīng)用程序類名(app的象征),該類必須是UIApplication(或子類)。如果為nil,則用UIApplication類作為默認(rèn)值

delegateClassName:

指定應(yīng)用程序的代理類,該類必須遵守UIApplicationDelegate協(xié)議
UIApplicationMain函數(shù)會(huì)根據(jù)principalClassName創(chuàng)建UIApplication對象,根據(jù)delegateClassName創(chuàng)建一個(gè)delegate對象,并將該delegate對象賦值給UIApplication對象中的delegate屬性
接著會(huì)建立應(yīng)用程序的Main Runloop(事件循環(huán)),進(jìn)行事件的處理(首先會(huì)在程序完畢后調(diào)用delegate對象的application:didFinishLaunchingWithOptions:方法)

程序正常退出時(shí)UIApplicationMain函數(shù)才返回。


二 UIApplication


1. 簡單介紹:

  1. UIApplication對象是應(yīng)用程序的象征,一個(gè)UIApplication對象就代表一個(gè)應(yīng)用程序。
  2. 每一個(gè)應(yīng)用都有自己的UIApplication對象,而且是單例的,如果試圖在程序中新建一個(gè)UIApplication對象,那么將報(bào)錯(cuò)提示。
  3. 通過[UIApplication sharedApplication]可以獲得這個(gè)單例對象
  4. 一個(gè)iOS程序啟動(dòng)后創(chuàng)建的第一個(gè)對象就是UIApplication對象,且只有一個(gè)(通過代碼獲取兩個(gè)UIApplication對象,打印地址可以看出地址是相同的)。
  5. 利用UIApplication對象,能進(jìn)行一些應(yīng)用級(jí)別的操作。

2. 應(yīng)用級(jí)別的操作:

1)設(shè)置應(yīng)用程序圖標(biāo)右上角的紅色提醒數(shù)字(如QQ消息的時(shí)候,圖標(biāo)上面會(huì)顯示1,2,3條新信息等)

/**
* iOS8以后需要注冊,才能將未讀的數(shù)在圖標(biāo)右上角顯示
*/
if (IS_IOS_8Later) {

// 使用本地通知 (本例中只是badge,但是還有alert和sound都屬于通知類型,其實(shí)如果只進(jìn)行未讀數(shù)在appIcon顯示,只需要badge就可, 這里全寫上為了方便以后的使用)
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
// 進(jìn)行注冊
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}

// 設(shè)置角標(biāo)
[UIApplication sharedApplication].applicationIconBadgeNumber = 200;

2)設(shè)置聯(lián)網(wǎng)指示器的可見性

// 設(shè)置聯(lián)網(wǎng)指示器可見性 - 顯示
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;·

3)管理狀態(tài)欄

如果想利用UIApplication來管理狀態(tài)欄,首先得修改Info.plist的設(shè)置:


// 黑色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;

// 白色 + 動(dòng)畫
[[UIApplication sharedApplication] setStatusBarStyle:(UIStatusBarStyleLightContent) animated:YES];

// 隱藏狀態(tài)欄
[UIApplication sharedApplication].statusBarHidden = YES;

// 隱藏 + 動(dòng)畫效果
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:(UIStatusBarAnimationFade)];

** 4)openURL:方法 **

UIApplication *app = [UIApplication sharedApplication];
// 打電話
[app openURL:[NSURL URLWithString:@"tel://10086"]];
// 發(fā)短信
[app openURL:[NSURL URLWithString:@"sms://10086"]];
// 發(fā)郵件
[app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];
// 打開一個(gè)網(wǎng)頁資源
[app openURL:[NSURL URLWithString:@"http://ios.itcast.cn"]];

openURL方法,也可以打開其他APP。

URL:統(tǒng)一資源定位符,用來唯一的表示一個(gè)資源。
URL格式:協(xié)議頭://主機(jī)地址/資源路徑
網(wǎng)絡(luò)資源:http/ ftp等 表示百度上一張圖片的地址http://www.baidu.com/images/20140603/abc.png
本地資源:file:///users/apple/desktop/abc.png(主機(jī)地址省略)


III. UIApplication Delegate


1. delegate方法:

UIApplication程序啟動(dòng)過程相關(guān)的一些delegate方法的調(diào)用時(shí)機(jī)。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"程序啟動(dòng)完成:%s",__func__);
return YES;
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"已經(jīng)獲得焦點(diǎn):%s",__func__);
}

- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"將要釋放焦點(diǎn):%s",__func__);
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"已經(jīng)進(jìn)入后臺(tái):%s",__func__);
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"將要進(jìn)入前臺(tái):%s",__func__);
}

- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序?qū)⒁顺觯?s",__func__);
}

2. 程序啟動(dòng):

程序被加載到內(nèi)存,完成啟動(dòng),application對象會(huì)自動(dòng)調(diào)用delegate的下面這個(gè)方法,證明程序已經(jīng)啟動(dòng)完成。所以這個(gè)方法也是首先會(huì)被application回調(diào)的方法,且這個(gè)方法在整個(gè)程序的生命周期中只會(huì)被調(diào)用一次。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSLog(@"程序啟動(dòng)完成:%s",__func__);
return YES;
}

程序啟動(dòng)時(shí),回調(diào)完上面的方法,會(huì)繼續(xù)回調(diào)delegate的已經(jīng)獲得了焦點(diǎn)的方法,證明程序已經(jīng)獲得了焦點(diǎn)。

- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"已經(jīng)獲得焦點(diǎn):%s",__func__);
}
結(jié)論:

應(yīng)用啟動(dòng)過程中,會(huì)依次調(diào)用delegate已經(jīng)啟動(dòng)完成和已經(jīng)獲得焦點(diǎn)的方法,不會(huì)調(diào)用已經(jīng)進(jìn)入前臺(tái)的方法。

3. 程序從前臺(tái)退出到后臺(tái):

當(dāng)程序處于前臺(tái)時(shí),單擊home鍵,程序會(huì)自動(dòng)退出到后臺(tái)。在這個(gè)過程中,程序會(huì)先回調(diào)delegate的將要失去焦點(diǎn)的方法,證明程序將要失去焦點(diǎn)。

- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"將要進(jìn)入前臺(tái):%s",__func__);
}

調(diào)用調(diào)用完上面的方法后,程序緊接著會(huì)調(diào)用delegate已經(jīng)進(jìn)入后臺(tái)的方法,證明程序已經(jīng)進(jìn)入后臺(tái)

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"已經(jīng)進(jìn)入后臺(tái):%s",__func__);
}
結(jié)論:

單擊home鍵進(jìn)入后臺(tái)會(huì)依次調(diào)用delegate的將要失去焦點(diǎn)的方法和已經(jīng)進(jìn)入后臺(tái)的方法。

4. 程序從后臺(tái)進(jìn)入到前臺(tái):

從后臺(tái)進(jìn)入前臺(tái)(無論是雙擊home鍵進(jìn)入或者點(diǎn)擊應(yīng)用圖標(biāo)進(jìn)入),會(huì)回調(diào)delegate的將要進(jìn)入前臺(tái)方法,證明程序?qū)⒁M(jìn)入前臺(tái)。

- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"將要進(jìn)入前臺(tái):%s",__func__);
}

回調(diào)完上面的方法,緊接著會(huì)繼續(xù)回調(diào)delegate的已經(jīng)獲得焦點(diǎn)的方法,證明程序已經(jīng)獲得了焦點(diǎn)。

- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"已經(jīng)獲得焦點(diǎn):%s",__func__);
}
結(jié)論:

從后臺(tái)進(jìn)入前臺(tái),會(huì)依次調(diào)用delegate的將要進(jìn)入前臺(tái)和已經(jīng)獲得焦點(diǎn)的方法。

5. 雙擊home鍵切換程序:

在前臺(tái),雙擊home鍵,只會(huì)調(diào)用delegate的將要失去焦點(diǎn)的方法,證明程序將要失去焦點(diǎn)

- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"將要釋放焦點(diǎn):%s",__func__);
}

當(dāng)用戶真正切換應(yīng)用時(shí)候,才會(huì)繼續(xù)調(diào)用delegate的已經(jīng)進(jìn)入后臺(tái)的方法,證明程序已經(jīng)進(jìn)入后臺(tái)。

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"已經(jīng)進(jìn)入后臺(tái):%s",__func__);
}
結(jié)論:

雙擊home鍵切換應(yīng)用。會(huì)分別調(diào)用程序?qū)⒁ソ裹c(diǎn)的方法和程序已經(jīng)進(jìn)入后臺(tái)的方法。 且這兩個(gè)方法是分開調(diào)用的。即,雙擊home鍵時(shí)調(diào)用將要失去焦點(diǎn)的方法,選擇其他應(yīng)用時(shí)調(diào)用已經(jīng)進(jìn)入后臺(tái)的方法。

6. 在前臺(tái)雙擊home鍵殺死程序:

雙擊home鍵時(shí),只會(huì)調(diào)用delegate的將要失去焦點(diǎn)的方法(上面已經(jīng)說過),證明程序?qū)⒁ソ裹c(diǎn)。

- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"將要釋放焦點(diǎn):%s",__func__);
}

然后手指上滑殺死程序,會(huì)直接調(diào)用delegate的已經(jīng)進(jìn)入后臺(tái)的方法,證明程序已經(jīng)進(jìn)入后臺(tái)

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"已經(jīng)進(jìn)入后臺(tái):%s",__func__);
}

然后緊接著調(diào)用delegate的程序?qū)⒁顺龅姆椒ǎ?strong>證明程序?qū)⒁粴⑺?/strong>。

- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序?qū)⒁顺觯?s",__func__);
}
結(jié)論:

雙擊home鍵然后殺死程序,會(huì)按照如下順序調(diào)用delegate的方法:

// 雙擊 home 鍵調(diào)用
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"將要釋放焦點(diǎn):%s",__func__);
}
// 殺死程序時(shí)調(diào)用這兩個(gè)方法
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"已經(jīng)進(jìn)入后臺(tái):%s",__func__);
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序?qū)⒁顺觯?s",__func__);
}

7. 從其他程序前臺(tái)雙擊home鍵殺死后臺(tái)程序:

如果從其他程序的前臺(tái),雙擊home鍵殺死后臺(tái)程序,被殺死程序只會(huì)回調(diào)delegate即將退出的方法。

- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序?qū)⒁顺觯?s",__func__);
}

為什么呢?

因?yàn)槲覀兪菑囊粋€(gè)前臺(tái)程序殺死一個(gè)后臺(tái)程序,這個(gè)后臺(tái)程序當(dāng)初進(jìn)入后臺(tái)時(shí)候已經(jīng)調(diào)用了將要釋放焦點(diǎn)和已經(jīng)進(jìn)入后臺(tái)的方法,所以殺死時(shí)候只會(huì)回調(diào)delegate即將終結(jié)的方法。

結(jié)論:

從一個(gè)前臺(tái)程序殺死一個(gè)后臺(tái)程序。后臺(tái)程序只會(huì)回調(diào)delegate的程序即將退出的方法。

8. 下拉通知欄:

下拉通知欄,只會(huì)回調(diào)delegate程序?qū)⒁尫沤裹c(diǎn)的方法。程序并沒有進(jìn)入后臺(tái),所以不會(huì)調(diào)用進(jìn)入后臺(tái)的方法。

- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"將要釋放焦點(diǎn):%s",__func__);
}

收起通知欄時(shí),只會(huì)調(diào)用已經(jīng)獲得焦點(diǎn)的方法,不會(huì)調(diào)用進(jìn)入前臺(tái)的方法。

- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"已經(jīng)獲得焦點(diǎn):%s",__func__);
}

同樣,從屏幕下方向上滑動(dòng)屏幕,喚出工具欄時(shí)候,也只會(huì)調(diào)用delegate的將要釋放焦點(diǎn)的方法。收起工具欄時(shí),只會(huì)調(diào)用delegate的已經(jīng)獲得焦點(diǎn)的方法。

結(jié)論:

下拉通知欄或者上拉工具欄,都只是回調(diào)delegate的即將釋放焦點(diǎn)的方法,程序不會(huì)進(jìn)入后臺(tái)。

9. 總結(jié)

當(dāng)初學(xué)習(xí)iOS時(shí)候,對這個(gè)地方不是很清楚,總是搞不懂為什么程序的delegate有一個(gè)將要進(jìn)入前臺(tái)的方法applicationWillEnterForeground:,卻沒有類似于applicationDidEnterForeground:的已經(jīng)進(jìn)入前臺(tái)的方法(純屬捏造)?為什么程序的delegate有一個(gè)已經(jīng)進(jìn)入后臺(tái)的方法applicationDidEnterBackground:卻沒有一個(gè)類似于applicationWillEnterBackground:的將要進(jìn)入后臺(tái)的方法?為什么進(jìn)入前臺(tái)時(shí),方法的調(diào)用順序是applicationWillEnterForeground:applicationDidBecomeActive:而不是相反?這些問題一直困擾著我。

將要進(jìn)入前臺(tái)、已經(jīng)獲得焦點(diǎn)、將要失去焦點(diǎn)、已經(jīng)進(jìn)入后臺(tái)這幾個(gè)方法是比較容易混淆的,且調(diào)用順序經(jīng)常被搞混。但是如果理解了蘋果為什么這么設(shè)計(jì),這些困惑都將迎刃而解。重點(diǎn)來了:如果一個(gè)應(yīng)用程序失去焦點(diǎn)那么意味著用戶當(dāng)前無法進(jìn)行交互操作,正因如此,程序從前臺(tái)退出到后臺(tái)時(shí)候,一般會(huì)先失去焦點(diǎn)再進(jìn)入后臺(tái)避免進(jìn)入后臺(tái)過程中用戶還可以和程序進(jìn)行交互。同理,一個(gè)應(yīng)用程序從后臺(tái)進(jìn)入前臺(tái)也是類似的,會(huì)先進(jìn)入前臺(tái)再獲得焦點(diǎn),這樣進(jìn)入前臺(tái)過程中未完全準(zhǔn)備好的情況下用戶無法操作,保證了程序的安全性。

至于為什么蘋果沒有提供類似于applicationDidEnterForeground:的已經(jīng)進(jìn)入前臺(tái)的方法,那是因?yàn)槌绦蜻M(jìn)入前臺(tái)后必定會(huì)回調(diào)delegate的已經(jīng)獲得焦點(diǎn)的方法,所以applicationDidBecomeActive:方法從本質(zhì)上就相當(dāng)于我們想象中的applicationDidEnterForeground:,如果我們想要在程序進(jìn)入前臺(tái)后做什么操作,完全可以把這些操作寫到applicationDidBecomeActive:里。同理,applicationWillResignActive:就相當(dāng)于我們想象中的applicationWillEnterForeground:。

另外一般如果應(yīng)用程序要保存用戶數(shù)據(jù)會(huì)在程序?qū)⒁ソ裹c(diǎn)的方法中進(jìn)行 (而不是在已經(jīng)進(jìn)入后臺(tái)的方法中執(zhí)行),因?yàn)槿绻脩綦p擊Home不會(huì)進(jìn)入后臺(tái)只會(huì)注銷激活。同理,如果用戶恢復(fù)應(yīng)用狀態(tài)一般在已經(jīng)獲的焦點(diǎn)的方法中執(zhí)行(而不是在將要進(jìn)入前臺(tái)的方法中執(zhí)行)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容