ios APP啓動原理與自定義UIApplication

iOS APP啓動原理

在oc項目中,入口文件是main.m文件,App啓動時首先會初始化所有的類,然後再調用main.m中的main函數。

啓動過程:

  1. 從類的初始化到main函數的執行
  2. 執行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綁定的事件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章