浅谈 iOS 应用启动过程

由于种种原因,简书等第三方平台博客不再保证能够同步更新,欢迎移步 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

首先要感谢没故事的卓同学大大送的泊学会员,在泊学学了几节课,了解了很多不同角度的 iOS 开发知识。这篇文章就启发自其 iOS 101 中的一个纯手工的 Single View Application 一文。但本文将更加深入的叙述了启动过程,且实现均为 Swift 3.0。

本文对应的 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 诞生以来,关于纯代码、Xib、以及 Storyboard 的选择就在 iOS 开发圈中炸开了锅。这里不会探讨各种姿势的优劣,只是可能很多和我一样的初学者,从一开始就被 Storyboard 先入为主。加上方便灵活的拖控件,自然而然就可能没有机会去思考一个 iOS 应用是如何启动起来的。加上 Swift 的诞生,使得整个项目的初始结构得到了更大的简化(少了 main.m 以及很多 .h 头文件)。
  • 为了便于研究 iOS 应用的启动过程,我们删除 Xcode 自动创建的 Main.storyboard,并把 Main Interface 清空。
Main Interface

AppDelegate.swift

  • AppDelegate.swift 中是 AppDelegate 类。
  • AppDelegate 将会创建 App 内容绘制的窗口,并提供应用内响应状态改变(state transitions)的地方。
  • AppDelegate 将会创建 App 的入口和 Run Loop(运行循环),并将输入事件发送到 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 是一个事件处理循环,可以用来在应用中安排任务并定位收到的即将到来的事件。

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
    }
}
  • 因为我们删除了 Main.storyboard,我们需要以上代码初始化 UIWindow(根 UIView),并使得其可见,关于 UIWindow 可以参考文末的链接。
  • AppDelegate 中的方法将应用程序对象和代理联系起来,当应用在不同状态会自动调用相应的代理方法,我们可以自定义相应的实现,抑或留空或删除即使用默认的实现。
  • application(_:​did​Finish​Launching​With​Options:​):该方法在应用启动进程几乎完成且将要运行之际调用,因此在其中初始化 window,设置根控制器,并使得其可见。

@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。
  • 我们可以尝试将该行注释,链接器将直接报错「entry point (_main) undefined.」,即入口 main 未定义,因此可以得知 @UIApplicationMain 标签将会根据其下方的 AppDelegate 创建一个 UIApplicationMain 入口并启动程序。
  • 手动实现 @UIApplicationMain:
    • 如果不使用 @UIApplicationMain 标签,需要自己建立一个 main.swift 文件,但我们不需要自己创建方法,Xcode 可以直接将该文件中的代码当作 main() 函数。
    • 在 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))
全局范围书写的代码将被当作程序入口,所以不需要 main() 函数。

UIApplication​Main()

  • 在 main.swift 中,调用了 int UIApplicationMain(int argc, char * _Nonnull argv[], NSString *principalClassName, NSString *delegateClassName); 方法。
  • 该方法在创建了应用程序对象、应用程序代理、以及设置了事件循环。
  • UIApplication​Main() 的前两个参数是命令行参数。
  • principalClassName: 该参数为 UIApplication 类名或其子类名的字符串,nil 是默认为 UIApplication。
  • delegateClassName: 该参数为要初始化的应用程序代理(AppDelegate)类,也可指定为 nil 但需要从应用程序的主 nib 文件加载代理对象。
  • 虽然该函数有返回值,但从不返回。

UIApplication

MyApp.swift

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

        super.sendEvent(event)
    }
}
  • 由上文得知,main.swift 中 UIApplication​Main()的第三个参数可以为 UIApplication 类名或其子类名的字符串。
  • 新建一个 MyApp.swift 在其中定义一个 UIApplication 子类,我们便可以在这里做一些针对应用全局的事情,比如重写 sendEvent(:) 方法便可以监听到整个应用发送事件。

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

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

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

// 设置后台取回间隔
@available(iOS 7.0, *)
open func setMinimumBackgroundFetchInterval(_ minimumBackgroundFetchInterval: TimeInterval)
// 通常将其设置为:
UIApplicationBackgroundFetchIntervalMinimum

// 后台时请求更长时间
@available(iOS 4.0, *)
open func beginBackgroundTask(expirationHandler handler: (() -> Swift.Void)? = nil) -> UIBackgroundTaskIdentifier
// 不要忘记结束时调用 endBackgroundTask(UIBackgroundTaskIdentifier)

// 状态来网络使用 Spinner 显示开关
var isNetworkActivityIndicatorVisible: Bool

var backgroundTimeRemaining: TimeInterval { get } // 直到暂停
var preferredContentSizeCategory: UIContentSizeCategory { get } // 大字体或小字体
var applicationState: UIApplicationState { get } // 前台,后台,已激活

Reference

也欢迎您关注我的微博 @萌面大道V & 简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容