iOS APP程序啓動原理

UIApplication 程序啓動原理

  一個應用程序運行就必需要有一個進程,一個進程至少要有一個線程,咱們把這個線程叫作主線程,主線程開啓以後會開啓一個主運行循環,若是不開啓一個運行循環,程序開啓了就立刻結束了,不會一直運行。換句話來講一個APP開啓就關閉了,因此必須開啓一個死循環,不斷的在監聽事件,用戶操做....等等一些事件,說完那就來看看程序怎麼啓動的。ios

  一個app用程序啓動就必需要有且僅有一個UIApplication(或則其子類)應用管理類,UIApplication 這個類的對象是一個單例對象。在程序裏可用 [UIApplication sharedApplication]來獲取這個單例對象,從這句話有能看出蘋果單例的命名是 shared 開頭windows

--------- 單例:就是無論聲明多少次只有一個對象,換句話就是隻有一分內存,xcode

那麼UIApplication對象什麼時候建立的?
一個程序都有一個main函數,iOS也不例外在xcode裏有一個main.m文件,這個main就是一個應用程序啓動的入口。
這個方法會調用了 UIApplicationMain,一切奧祕都在這個方法裏,咱們先看看這個方法是怎麼調用的網絡

/// argcargv是在終端傳參的時候有用
// argc: 傳入的參數,默認是1,若是在終端中調用當前應用程序傳入其餘參數就是其餘參數個數 + 1
// argv: 默認第一個參數是程序的路徑,其餘參數是傳進來的參數
// principalClassName: 默認就是@"UIApplication"
// delegateClassName: 利用類名轉字符串不容易寫錯 傳進去一個字符串利用反射機制把字符串反射成UIApplication的代理對象
UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);app

UIApplicationMain底層實現,主要是用到了反射機制利用字符串生成類建立對象ide

  • 1. 首先會根據principalClassName的參數 去建立一個 Application 單例對象
  • 2. 建立完Application對象接着會根據 delegateClassName 參數,建立一個Application的代理對象,而且指定Application的代理就是這個代理對象
  • 3. 啓動一個主運行循環(可以處理事件、監聽用戶操做的一個死循環) 監聽系統事件,調用application對應代理方法
  • 4. 加載info.plist文件:設置一些程序信息,而且判斷有沒用指定main.storyboard,加載storboard
    1. 初始化一個窗口
    2. 建立控制器,設置窗口根控制器
    3. 顯示主窗口 

上面提到一個UIApplication類,那麼UIApplication對象有什麼用?
UIApplication的一個主要工做是處理用戶事件,它會起一個隊列,把全部用戶事件都放入隊列,逐個處理,在處理的時候,它會發送當前事件到一個合適的處理事件的目標控件。此外,UIApplication實例還維護一個在本應用中打開的window列表(UIWindow實例),這樣它就能夠接觸應用中的任何一個UIView對象。UIApplication實例會被賦予一個代理對象,以處理應用程序的生命週期事件,還能夠作其餘應用級別的任務。因此UIApplication的核心做用是提供了iOS程序運行期間的控制和協做工做。函數

1、 下面是利用UIApplication 設置一些應用的功能oop

1. 發送短信動畫

[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@"sms://466453"]];

2. APP圖標消息數量提醒框google

// 設置APP圖標提醒數字,IOS8之後要想設置applicationIconBadgeNumber這個屬性,
// 必須註冊一個用戶通知 -[UIApplication registerUserNotificationSettings:]
UIApplication *app = [UIApplication sharedApplication];
// 建立一個用戶通知
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
// 註冊通知
[app registerUserNotificationSettings:settings];
app.applicationIconBadgeNumber = 10;

3. 網絡狀態設置

app.networkActivityIndicatorVisible = YES;

- 4.控制狀態欄
———————— 第一種方法 交給UIViewController控制器控制管理
- IOS7應用程序的狀態欄默認是交給UIViewController控制器控制管理
- 若是要交給Application管理須要在info.plist文件增長一條屬性

// 帶有動畫效果的隱藏
// [app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
[app setStatusBarHidden:YES];

———————— 第二中方法 經過控制器去重寫方法prefersStatusBarHidden方法完成隱藏狀態欄

// 控制器 - 隱藏狀態欄
- (BOOL)prefersStatusBarHidden
{ return YES; }

// 控制器 - 狀態欄的樣式
- (UIStatusBarStyle)preferredStatusBarStyle
{ return UIStatusBarStyleLightContent; }

- 5.設置搖動手勢的時候,是否支持redo,undo操做

[UIApplication sharedApplication].applicationSupportsShakeToEdit = YES;

- 6.判斷程序運行狀態

/// UIApplicationStateActive,
/// UIApplicationStateInactive,
/// UIApplicationStateBackground
if([UIApplication sharedApplication].applicationState == UIApplicationStateInactive){
  NSLog(@"程序在運行狀態");
}

- 7.阻止屏幕變暗進入休眠狀態 -> 儘可能別使用本功能,由於很耗電。

[UIApplication sharedApplication].idleTimerDisabled = YES;

- 8.在map上顯示一個地址

NSString *addressText = @"1 Infinite Loop, Cupertino, CA 95014";
addressText = [addressTextstringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString *urlText = [NSStringstringWithFormat:@"http://maps.google.com/maps?q=%@", addressText];
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:urlText]];

- 9.發送電子郵件

NSString *recipients =@"mailto:first@example.com?cc=second@example.com,third@example.com&subject=Hello from California!";
NSString *body =@"&body=It is raining in sunny California!";
NSString *email = [NSStringstringWithFormat:@"%@%@", recipients, body];
email = [emailstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:email]];

- 10.打電話到一個號碼

[[UIApplication sharedApplication] openURL:[NSURLURLWithString:@"tel://10086"]];

- 11.打開一個網址

[[UIApplication sharedApplication] openURL:[NSURLURLWithString:@"http://www.baidu.com"]];

 

2、 UIApplicationdelegate
上面介紹程序啓動的時候在main函數裏會調用UIApplicationMain方法傳進去的後2個參數就是UIApplicationUIApplicationDelegate 2個類的字符串,以後利用反射把UIApplication的實例對象設置爲UIApplication的代理,因此在Xcode建立以後會帶2個文件【AppDelegate.h】和【AppDelegate.m】這2個文件,這2個文件就是系統生成的UIApplication代理文件,裏面遵照了【<UIApplicationDelegate>】這個協議,因此他能幫助UIApplication在程序啓動的時候能監聽不少事,好比

  • - 應用程序的生命週期
  • - 內存警告
  • - 系統事件
  • - 等等......

下面就是UIApplication代理能作的一些經常使用事件

// app程序完成後調用
1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 咱們通常在這裏作一些程序啓動以後須要的設置
// 或者純代碼編寫的時候,對窗口的初始化,控制器的指定.........

return YES;
}

// 當程序載入後執行
2. - (void)applicationDidFinishLaunching:(UIApplication*)application

// app失去焦點的時候
3. - (void)applicationWillResignActive:(UIApplication *)application;

// 返回後臺調用
// app被打斷的時候,在這裏能夠保存一些數據
4. - (void)applicationDidEnterBackground:(UIApplication *)application;

// 進入前臺的時候調用
5. - (void)applicationWillEnterForeground:(UIApplication *)application;

// 應用程序獲取到焦點的時候調用,
// 換句話來講就是回到前臺能與用戶交互的時候調用
6. - (void)applicationDidBecomeActive:(UIApplication *)application;

// 被銷燬的時候調用
// 幾乎感覺這個事件的調用,由於事件過短了,因此若是程序關閉須要作些事情最好別在這裏寫,可能沒等執行程序就關閉了
// 這個須要要設置UIApplicationExitsOnSuspend的鍵值
7. - (void)applicationWillTerminate:(UIApplication *)application;

// 內存警告調,由於iPhone內存有限,因此內存管理很是重要,不慎重程序內存越積越多,達到必定度,程序就會閃退,崩潰,其餘問題,因此請珍惜每一分內存的使用,若是內存達到必定程度,你能夠利用這個函數清理內存
8. - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application;

// 請求委託打開一個URL資源
// 當經過url執行
9. - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url;  
10. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; 

// 當系統時間發生改變時執行
11. - (void)applicationSignificantTimeChange:(UIApplication *)application;

// 設置 StatusBar 狀態
// 當StatusBar框方向將要變化時執行
// 當StatusBar框將要變化時執行
12. - (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame;
// 當StatusBar框方向將要變化時執行
13. - (void)application:(UIApplication *)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration; 
// 當StatusBar框方向改變時執行
// 當StatusBar框變化完成後執行
14. - (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame; 
// 當StatusBar框方向變化完成後執行 
15. - (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation; 

// 當一個應用程序成功的註冊一個推送服務(APS) 發送到代理去
16. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
// 當一個應用程序註冊一個推送服務(APS) 發送到代理中失敗時執行
17. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;

// 當一個運行着的應用程序收到一個遠程的通知 發送到代理去...
18. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
// 當一個運行着的程序接受一個本地的通知時執行
19. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

// 通知代理,受保護的文件當前變爲不可用的
20. - (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application;
// 通知代理,受保護的文件當前變爲可用的
21. - (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application;

上面這些代理方法,就是AppDelegate須要幫助UIApplication作的事

 

3、接下來講說 UIWindow 窗口
UIWindow 一個特殊UIView,也是繼承自UIView。
ios程序啓動以後第一個建立的控件就是UIWindow,以後在建立控制器的View,而後在把控制器的View加載到UIWindow上,而後顯示,因此其實咱們看到的界面都是顯示在UIWindow上的。

界面顯示的3個重要對象

  • UIScreen: 鏈接設備 ——app與設備屏幕鏈接點
  • UIWindow: 給屏幕提供繪製、顯示的接口,屏幕上的全部的對象都是繪製上去的,沒有窗口就不能顯示東西
  • UIView:提供一些繪圖操做,繪製完畢在繪製到UIWindow上顯示

storyboard加載過程
若是在info.plist文件裏聲明瞭storyboard顯示項,程序會自動將指定的storboard加載並顯示箭頭指向的控制器
若是沒有指向,其實在大項目中不多有直接指定某個storyboard顯示項的都是在程序啓動的時候手動建立,也就是UIApplication的代理方法裏去手動加載storyboard,顯示指定控制器。下面就是模仿系統加載 storyboard 的過程
1. 初始化一個窗口
注意:

  • 若是窗口不被建立以後就被銷燬就必須強引用,因此須要用一個成員屬性強引用住,其實系統有一個本身建立的window,因此不用本身手動建立直接用系統的就行
  • 必需要設置尺寸
  • 若是給系統的self.window賦值,系統會直接把這個window添加到application.windows裏面
// 窗口的層級關係 (往下層級越高)
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERNconst UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar;
UIWindow *window = [[UIWindow all] initWithFrame:[UIScreen main].bounds];

 2. 加載main.storyboard,而且建立指定的控制器

// 加載Mainstoryboard
UIStoryboard *stotyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// 獲取storyboard箭頭指向的控制器
// UIViewController *vc = [stotyboard instantiateViewControllerWithIdentifier:@"ViewController"]; // 獲取Storyboard ID 爲ViewController
UIViewController *vc = [stotyboard instantiateInitialViewController];
// 設置根控制器
window.rootViewController = vc; 
// 有人會想把控制器的view直接添加到window上,那樣會有不少弊端的,因此別那樣弄
   - 例如程序旋轉的時候view不會跟着一塊兒旋轉

 3. 把建立的控制器做爲窗口的根控制器,顯示窗口

// 設置application的主窗口
// 這個方法內部會把window.hidden設置爲YES
[window makeKeyAndVisible];

 

總結:
storyboard加載過程其實就3步驟

  1. 初始化一個窗口
  2. 加載main.storyboard,而且建立指定的控制器
  3. 把建立的控制器做爲窗口的根控制器,顯示窗口


最後咱們來個總結:
程序啓動首先去main.m文件去執行main方法 ———> 接着會執行UIApplicationMain的方法 ————> 【在裏面會把傳進的後面2個【一個UIApplication,一個是AppDelegate】參數實例,
而且設置AppDelegate 做爲 UIApplication 的代理,而後啓動一個主運行循環,監聽事件,其餘操做】

相關文章
相關標籤/搜索