iOS APP啓動原理
在oc項目中,入口文件是main.m文件,App啓動時首先會初始化所有的類,然後再調用main.m中的main函數。
啓動過程:
- 從類的初始化到main函數的執行
- 執行AppDelegate中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
main.m代碼
/*
UIApplicationMain
1 創建了 UIApplication 應用對象;
2 創建了Application delegate(AppDelegate)對象
3 建立了一個事件循環 (創建了runloop對象,開啓事件循環)
4 讀info.plist
創建一個AppDelegate的window
UIWindowLevelAlert > UIWindowLevelStatusBar > UIWindowLevelNormal
*/
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); // 死循環
}
}
雖然main中有return,但其實沒有返回任何東西,若return則代表着程序退出。主要原因在於runloop,其內部會有while循環保證程序一直運行。
第一個參數 ——argc,int類型。代表程序啓動時的參數個數。默認是1
第二個參數——argv,char[]。代表各個參數的值,默認是程序的名字
第三個參數——principalClassName,NSString類型。主要傳UIApplication或其子類的類名對應的字符串。如果nil,就是 UIApplication。(可以自定義UIApplication的子類,處理一些事情如:日誌的統計)
第四個參數——delegateClassName,NSString類型。AppDelegate類由編譯器自動生成模板。並實現裏面的協議以實現我們需要的功能。由於許多配置需要在AppDelegate中進行設置,因此會造成文件代碼的臃腫,可以創建APPDelegate的分類,在分類中配置一些第三方庫需要的配置參數,或者廣告頁等,以達到瘦身的目的。
在xcode5之前,通常會寫這樣一段代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController * rootVC = [ViewController new];
self.window.rootViewController = rootVC;
[self.window makeKeyAndVisible];
return YES;
}
有了storyboard之後就不需要了。爲什麼不需要了呢?APPDelegate是如何與ViewController建立聯繫的。主要還是回到main函數中的UIApplicationMain這個類,在初始化的時候會加載info.plist這個資源文件,如下圖所示
在資源文件中Main storyboard file base name 指定了加載Main.StoryBoard作爲應用的入口文件,而Main.StoryBoard又與ViewContoller進行了關聯,所以不需要做其它操作。
如果我們把這個配置給刪除掉,那麼則需要在AppDelegate的協議中寫上上面那段代碼。
在swift項目中其實沒有main文件。而在APPDelegate.swift中多一個註解@UIApplicationMain,系統會默認將該文件作爲UIApplicationMain的第四個參數。如果想要自定義,需要註釋掉@UIApplicationMain,然後創建main.swift
import UIKit
import Foundation
UIApplicationMain(CommandLine.argc,
UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)),
nil,
NSStringFromClass(AppDelegate.self)
)
這樣就OK了。如果不使用自定義的UIApplication,那麼創建main.swift文件的意義就不大。
自定義UIApplication
UIApplicationMain的第三個參數是UIApplication對象。如果傳nil,則會默認調用UIApplication對象。如果我們自定義UIApplication可以提完成我們自定義的。在定義的UIApplication對象我們可以統一處理一些業務,比如日誌等
import UIKit
class CustomApplication: UIApplication {
//uiapplicton中有與Event相關的api,所以可以利用這些api完成記錄行爲日誌的任務
override func sendEvent(_ event: UIEvent) {
//在這裏處理一些統一的邏輯
print("來自UIApplication 的 event")
return super .sendEvent(event)
}
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
//在這裏處理一些統一的邏輯
//例如 longinController的,某個action
if target is ViewController && sender is UIButton && NSStringFromSelector(action) == "loginAction"{
//記錄日誌或者上傳服務器
print("來自UIApplication 的 sendAction")
}
return super.sendAction(action, to: target, from: sender, for: event)
}
}
在ViewController中添加一個button,並點擊按鈕
UIApplication的sendEvent被調用多次,然後sendAction方法被處罰,最後執行button綁定的事件