因为Cocoa 开发环境已经在新建一个项目时帮助我们进行了很多配置
,这导致了不少刚接触iOS 的开发者都存在基础比较薄弱
的问题,其中一个最显著的现象就是很多人无法说清一个App 启动的流程。程序到底是怎么开始的,AppDelegate 到底是什么,xib 或者storyboard 是怎么被加载到屏幕上的? 这一系列的问题,我们在开发中虽然不会每次都会去关心和配置,但是如果能进行一些了解的话,对于程序各个部分的职责的明确会很有帮助。
在C 语言中,程序的入口都是main 函数
。对于一个Objective-C
的iOS app
项目,在新建项目时,Xcode 将帮助我们准备好一个main.m
文件,其中就有这个main 函数
:
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
在这里我们调用了UIKit
的UIApplicationMain 方法
。这个方法将根据第三个
参数初始化一个UIApplication 或其子类的对象
开始接收事件
(在这个例子中传入nil
,意味使用默认的UIApplication
)。最后一个参数指定了AppDelegate 类
作为应用的委托
,它被用来接收类似didFinishLaunching
或者didEnterBackground
这样的与应用生命周期
相关的委托方法
。另外,虽然这个方法标明为返回一个int
,但是其实它并不会真正返回
。它会一直存在于内存中,直到用户或者系统将其强制终止。
了解了这些后,我们就可以来看看Swift 项目中对应的情况了。新建一个Swift 的iOS App 项目
后,我们会发现所有文件中都没有一个像Objective-C 中
那样的main 文件
,也不存在main 函数
。唯一
和main
有关系的是在默认的AppDelegate 类的声明上方有一个@UIApplicationMain
或@main
的标签(@main
标签是通用标签,当为iOS
App项目时将自动执行@UIApplicationMain
标签的内容,当为macOS
项目时,将自动执行@NSApplicationMain
标签的内容,@UIApplicationMain
标签与@NSApplicationMain
标签不互通)。
不说可能你也已经猜到,这个标签
做的事情就是将被标注的类作为委托
,去创建一个UIApplication
并启动
整个程序
。在编译的时候,编译器将寻找这个标记的类
,并自动插入像 main 函数
这样的模板代码。我们可以试试把@UIApplicationMain
去掉会怎么样:
Entry point (_main) undefined. for architecture x86_64
这说明找不到main 函数了。
在一般情况下,我们并不需要对这个标签做任何修改,但是当我们想使用UIApplication 的子类而不是它本身的话,我们就需要对这部分内容做点“手脚”
了。
刚才说到,其实Swift
的App 也是需要main 函数
的,只不过默认情况下是@UIApplicationMain
帮助我们自动生成
了而已。和C
语言的main.c
或者main.m 文件
一样,Swift 项目也可以有一个名为main.swift 的特殊文件
。在这个文件中,我们不需要定义作用域,而可以直接书写代码。这个文件中的代码将作为main 函数
来执行。比如我们在删除@UIApplicationMain
后,在项目中添加一个main.swift
文件,然后加上这样的代码:
import Foundation
import UIKit
UIApplicationMain(CommandLine.argc, UnsafeMutablePointer(CommandLine.unsafeArgv), NSStringFromClass(UIApplication.self), NSStringFromClass(AppDelegate.self))
现在编译运行后,就不会再出现错误了。当然,我们还可以将第三个参数
替换成自己的UIApplication 子类
,这样我们就可以轻易的做一些控制整个应用行为
的事情了,比如将main.swift 的内容换成:
class MyApplication: UIApplication {
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
print("Event sent: \(event)")
}
}
UIApplicationMain(CommandLine.argc, UnsafeMutablePointer(CommandLine.unsafeArgv), NSStringFromClass(MyApplication.self), NSStringFromClass(AppDelegate.self))
这样每次发送事件(比如点击按钮)时,我们都可以监听到这个事件了。