淺談 iOS 應(yīng)用啟動(dòng)過(guò)程

由于種種原因,簡(jiǎn)書等第三方平臺(tái)博客不再保證能夠同步更新,歡迎移步 GitHub:https://github.com/kingcos/Perspective/。謝謝!

Create an iOS single view application manually in Swift.

Date Notes Swift Xcode
2017-05-26 CS193p UIApplication 3.1 8.3.2
2017-03-28 首次提交 3.0 8.2.1

Preface

首先要感謝沒(méi)故事的卓同學(xué)大大送的泊學(xué)會(huì)員,在泊學(xué)學(xué)了幾節(jié)課,了解了很多不同角度的 iOS 開發(fā)知識(shí)。這篇文章就啟發(fā)自其 iOS 101 中的一個(gè)純手工的 Single View Application 一文。但本文將更加深入的敘述了啟動(dòng)過(guò)程,且實(shí)現(xiàn)均為 Swift 3.0。

本文對(duì)應(yīng)的 Demo 可以在:https://github.com/kingcos/SingleViewAppManually-Demo 查看、下載。

Manually or Storyboard

main.m in Objective-C Single View Application

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

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
  • 自從 Storyboard 誕生以來(lái),關(guān)于純代碼、Xib、以及 Storyboard 的選擇就在 iOS 開發(fā)圈中炸開了鍋。這里不會(huì)探討各種姿勢(shì)的優(yōu)劣,只是可能很多和我一樣的初學(xué)者,從一開始就被 Storyboard 先入為主。加上方便靈活的拖控件,自然而然就可能沒(méi)有機(jī)會(huì)去思考一個(gè) iOS 應(yīng)用是如何啟動(dòng)起來(lái)的。加上 Swift 的誕生,使得整個(gè)項(xiàng)目的初始結(jié)構(gòu)得到了更大的簡(jiǎn)化(少了 main.m 以及很多 .h 頭文件)。
  • 為了便于研究 iOS 應(yīng)用的啟動(dòng)過(guò)程,我們刪除 Xcode 自動(dòng)創(chuàng)建的 Main.storyboard,并把 Main Interface 清空。
Main Interface

AppDelegate.swift

  • AppDelegate.swift 中是 AppDelegate 類。
  • AppDelegate 將會(huì)創(chuàng)建 App 內(nèi)容繪制的窗口,并提供應(yīng)用內(nèi)響應(yīng)狀態(tài)改變(state transitions)的地方。
  • AppDelegate 將會(huì)創(chuàng)建 App 的入口和 Run Loop(運(yùn)行循環(huán)),并將輸入事件發(fā)送到 App(由 @UIApplicationMain 完成)。

Run Loop:
An event processing loop that you use to schedule work and coordinate the receipt of incoming events in your app. (From Start Developing iOS Apps (Swift))
Run Loop 是一個(gè)事件處理循環(huán),可以用來(lái)在應(yīng)用中安排任務(wù)并定位收到的即將到來(lái)的事件。

AppDelegate.swift

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow()
        window?.backgroundColor = UIColor.red
        window?.rootViewController = UIViewController()
        window?.makeKeyAndVisible()

        return true
    }
}
  • 因?yàn)槲覀儎h除了 Main.storyboard,我們需要以上代碼初始化 UIWindow(根 UIView),并使得其可見(jiàn),關(guān)于 UIWindow 可以參考文末的鏈接。
  • AppDelegate 中的方法將應(yīng)用程序?qū)ο蠛痛砺?lián)系起來(lái),當(dāng)應(yīng)用在不同狀態(tài)會(huì)自動(dòng)調(diào)用相應(yīng)的代理方法,我們可以自定義相應(yīng)的實(shí)現(xiàn),抑或留空或刪除即使用默認(rèn)的實(shí)現(xiàn)。
  • application(_:?did?Finish?Launching?With?Options:?):該方法在應(yīng)用啟動(dòng)進(jìn)程幾乎完成且將要運(yùn)行之際調(diào)用,因此在其中初始化 window,設(shè)置根控制器,并使得其可見(jiàn)。

@UIApplicationMain

main.swift

import UIKit

UIApplicationMain(
    CommandLine.argc,
    UnsafeMutableRawPointer(CommandLine.unsafeArgv)
        .bindMemory(
            to: UnsafeMutablePointer<Int8>.self,
            capacity: Int(CommandLine.argc)),
    nil,
    NSStringFromClass(AppDelegate.self)
)
  • 在 AppDelegate.swift 文件中 AppDelegate 類聲明之上的一行便是 @UIApplicationMain。
  • 我們可以嘗試將該行注釋,鏈接器將直接報(bào)錯(cuò)「entry point (_main) undefined.」,即入口 main 未定義,因此可以得知 @UIApplicationMain 標(biāo)簽將會(huì)根據(jù)其下方的 AppDelegate 創(chuàng)建一個(gè) UIApplicationMain 入口并啟動(dòng)程序。
  • 手動(dòng)實(shí)現(xiàn) @UIApplicationMain:
    • 如果不使用 @UIApplicationMain 標(biāo)簽,需要自己建立一個(gè) main.swift 文件,但我們不需要自己創(chuàng)建方法,Xcode 可以直接將該文件中的代碼當(dāng)作 main() 函數(shù)。
    • 在 main.swift 中,我們添加以上的代碼。

Code written at global scope is used as the entry point for the program, so you don’t need a main() function. (From The Swift Programming Language (Swift 3.0.1))
全局范圍書寫的代碼將被當(dāng)作程序入口,所以不需要 main() 函數(shù)。

UIApplication?Main()

  • 在 main.swift 中,調(diào)用了 int UIApplicationMain(int argc, char * _Nonnull argv[], NSString *principalClassName, NSString *delegateClassName); 方法。
  • 該方法在創(chuàng)建了應(yīng)用程序?qū)ο?、?yīng)用程序代理、以及設(shè)置了事件循環(huán)。
  • UIApplication?Main() 的前兩個(gè)參數(shù)是命令行參數(shù)。
  • principalClassName: 該參數(shù)為 UIApplication 類名或其子類名的字符串,nil 是默認(rèn)為 UIApplication。
  • delegateClassName: 該參數(shù)為要初始化的應(yīng)用程序代理(AppDelegate)類,也可指定為 nil 但需要從應(yīng)用程序的主 nib 文件加載代理對(duì)象。
  • 雖然該函數(shù)有返回值,但從不返回。

UIApplication

MyApp.swift

class MyApp: UIApplication {
    override func sendEvent(_ event: UIEvent) {
        print("\(#function) - Event: \(event)")

        super.sendEvent(event)
    }
}
  • 由上文得知,main.swift 中 UIApplication?Main()的第三個(gè)參數(shù)可以為 UIApplication 類名或其子類名的字符串。
  • 新建一個(gè) MyApp.swift 在其中定義一個(gè) UIApplication 子類,我們便可以在這里做一些針對(duì)應(yīng)用全局的事情,比如重寫 sendEvent(:) 方法便可以監(jiān)聽到整個(gè)應(yīng)用發(fā)送事件。

Update for CS193p

let myApp = UIApplication.shared
  • UIApplication 在 App 中是單例的。
  • UIApplication 管理所有全局行為。
  • UIApplication 不需要子類化。
// 在其他 App 中打開 URL
open func open(_ url: URL, options: [String : Any] = [:], completionHandler completion: ((Bool) -> Swift.Void)? = nil)

@available(iOS 3.0, *)
open func canOpenURL(_ url: URL) -> Bool

// 注冊(cè)接收推送通知
@available(iOS 8.0, *)
open func registerForRemoteNotifications()

@available(iOS 3.0, *)
open func unregisterForRemoteNotifications()
// 本地或推送的通知由 UNNotification 處理

// 設(shè)置后臺(tái)取回間隔
@available(iOS 7.0, *)
open func setMinimumBackgroundFetchInterval(_ minimumBackgroundFetchInterval: TimeInterval)
// 通常將其設(shè)置為:
UIApplicationBackgroundFetchIntervalMinimum

// 后臺(tái)時(shí)請(qǐng)求更長(zhǎng)時(shí)間
@available(iOS 4.0, *)
open func beginBackgroundTask(expirationHandler handler: (() -> Swift.Void)? = nil) -> UIBackgroundTaskIdentifier
// 不要忘記結(jié)束時(shí)調(diào)用 endBackgroundTask(UIBackgroundTaskIdentifier)

// 狀態(tài)來(lái)網(wǎng)絡(luò)使用 Spinner 顯示開關(guān)
var isNetworkActivityIndicatorVisible: Bool

var backgroundTimeRemaining: TimeInterval { get } // 直到暫停
var preferredContentSizeCategory: UIContentSizeCategory { get } // 大字體或小字體
var applicationState: UIApplicationState { get } // 前臺(tái),后臺(tái),已激活

Reference

也歡迎您關(guān)注我的微博 @萌面大道V & 簡(jiǎn)書

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

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

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