I. 程序啟動(dòng)
1. 程序啟動(dòng)的完整過(guò)程:
- 創(chuàng)建 UIApplication 對(duì)象
- 創(chuàng)建 UIApplication 的 delegate 對(duì)象 delegate對(duì)象開(kāi)始處理(監(jiān)聽(tīng))系統(tǒng)事件(沒(méi)有 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對(duì)象幢尚,根據(jù)delegateClassName創(chuàng)建一個(gè)delegate對(duì)象瑰妄,并將該delegate對(duì)象賦值給UIApplication對(duì)象中的delegate屬性
接著會(huì)建立應(yīng)用程序的Main Runloop(事件循環(huán))米同,進(jìn)行事件的處理(首先會(huì)在程序完畢后調(diào)用delegate對(duì)象的application:didFinishLaunchingWithOptions:
方法)
程序正常退出時(shí)UIApplicationMain函數(shù)才返回求冷。
二 UIApplication
1. 簡(jiǎn)單介紹:
- UIApplication對(duì)象是應(yīng)用程序的象征,一個(gè)UIApplication對(duì)象就代表一個(gè)應(yīng)用程序窍霞。
- 每一個(gè)應(yīng)用都有自己的UIApplication對(duì)象匠题,而且是單例的,如果試圖在程序中新建一個(gè)UIApplication對(duì)象但金,那么將報(bào)錯(cuò)提示韭山。
- 通過(guò)
[UIApplication sharedApplication]
可以獲得這個(gè)單例對(duì)象 - 一個(gè)iOS程序啟動(dòng)后創(chuàng)建的第一個(gè)對(duì)象就是UIApplication對(duì)象,且只有一個(gè)(通過(guò)代碼獲取兩個(gè)UIApplication對(duì)象冷溃,打印地址可以看出地址是相同的)钱磅。
- 利用UIApplication對(duì)象,能進(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以后需要注冊(cè),才能將未讀的數(shù)在圖標(biāo)右上角顯示
*/
if (IS_IOS_8Later) {
// 使用本地通知 (本例中只是badge褪迟,但是還有alert和sound都屬于通知類型,其實(shí)如果只進(jìn)行未讀數(shù)在appIcon顯示,只需要badge就可, 這里全寫(xiě)上為了方便以后的使用)
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
// 進(jìn)行注冊(cè)
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
// 設(shè)置角標(biāo)
[UIApplication sharedApplication].applicationIconBadgeNumber = 200;
2)設(shè)置聯(lián)網(wǎng)指示器的可見(jiàn)性
// 設(shè)置聯(lián)網(wǎng)指示器可見(jiàn)性 - 顯示
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;·
3)管理狀態(tài)欄
如果想利用UIApplication來(lái)管理狀態(tài)欄冗恨,首先得修改Info.plist的設(shè)置:
// 黑色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
// 白色 + 動(dòng)畫(huà)
[[UIApplication sharedApplication] setStatusBarStyle:(UIStatusBarStyleLightContent) animated:YES];
// 隱藏狀態(tài)欄
[UIApplication sharedApplication].statusBarHidden = YES;
// 隱藏 + 動(dòng)畫(huà)效果
[[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"]];
// 打開(kāi)一個(gè)網(wǎng)頁(yè)資源
[app openURL:[NSURL URLWithString:@"http://ios.itcast.cn"]];
openURL方法,也可以打開(kāi)其他APP味赃。
URL:統(tǒng)一資源定位符掀抹,用來(lái)唯一的表示一個(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ò)程相關(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
對(duì)象會(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)過(guò)程中敢订,會(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è)過(guò)程中,程序會(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)(無(wú)論是雙擊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è)方法是分開(kāi)調(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)說(shuō)過(guò)),證明程序?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)的方法虽界。程序并沒(méi)有進(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í)候都毒,對(duì)這個(gè)地方不是很清楚色罚,總是搞不懂為什么程序的delegate有一個(gè)將要進(jìn)入前臺(tái)的方法applicationWillEnterForeground:
,卻沒(méi)有類似于applicationDidEnterForeground:
的已經(jīng)進(jìn)入前臺(tái)的方法(純屬捏造)账劲?為什么程序的delegate有一個(gè)已經(jīng)進(jìn)入后臺(tái)的方法applicationDidEnterBackground:
卻沒(méi)有一個(gè)類似于applicationWillEnterBackground:
的將要進(jìn)入后臺(tái)的方法戳护?為什么進(jìn)入前臺(tái)時(shí),方法的調(diào)用順序是applicationWillEnterForeground:
和applicationDidBecomeActive:
而不是相反瀑焦?這些問(wèn)題一直困擾著我腌且。
將要進(jìn)入前臺(tái)、已經(jīng)獲得焦點(diǎn)榛瓮、將要失去焦點(diǎn)铺董、已經(jīng)進(jìn)入后臺(tái)這幾個(gè)方法是比較容易混淆的,且調(diào)用順序經(jīng)常被搞混禀晓。但是如果理解了蘋(píng)果為什么這么設(shè)計(jì)精续,這些困惑都將迎刃而解。重點(diǎn)來(lái)了:如果一個(gè)應(yīng)用程序失去焦點(diǎn)那么意味著用戶當(dāng)前無(wú)法進(jìn)行交互操作粹懒,正因如此重付,程序從前臺(tái)退出到后臺(tái)時(shí)候,一般會(huì)先失去焦點(diǎn)再進(jìn)入后臺(tái)避免進(jìn)入后臺(tái)過(guò)程中用戶還可以和程序進(jìn)行交互崎淳。同理,一個(gè)應(yīng)用程序從后臺(tái)進(jìn)入前臺(tái)也是類似的愕把,會(huì)先進(jìn)入前臺(tái)再獲得焦點(diǎn)拣凹,這樣進(jìn)入前臺(tái)過(guò)程中未完全準(zhǔn)備好的情況下用戶無(wú)法操作,保證了程序的安全性恨豁。
至于為什么蘋(píng)果沒(méi)有提供類似于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)后做什么操作菊匿,完全可以把這些操作寫(xiě)到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í)行)。