iOS开发基础23-核心概念:Info.plist、UIApplication、UIWindow 与启动流程
iOS 核心概念:Info.plist、UIApplication、UIWindow 与启动流程
本文系统梳理 iOS 开发中四个基础核心概念:Info.plist 配置、UIApplication 单例、UIApplicationDelegate 生命周期、UIWindow 层级,以及完整的 App 启动流程,并补充 iOS 13+ Scene 架构的现代变化。
一、Info.plist 常见设置
1. 什么是 Info.plist
每个 iOS 项目都有一个名为 "{工程名}-Info.plist" 的配置文件(Information Property List),用于运行时配置应用的基本信息,如 Bundle ID、版本号、图标、权限声明等。该文件是 App 签名和系统识别的重要依据,不能删除。
- 旧版 Xcode 中文件名为
Info.plist。 InfoPlist.strings用于 Info.plist 的本地化(如应用名称的多语言)。- 自定义 plist 文件应避免命名为
Info.plist,以免与主配置混淆。
2. 常见配置键
| 配置项 | Key | 说明 |
|---|---|---|
| Bundle ID | CFBundleIdentifier |
应用唯一标识,上架和推送必需 |
| 版本号(用户可见) | CFBundleShortVersionString |
如 1.2.0,App Store 显示 |
| 构建号 | CFBundleVersion |
如 15,每次提交递增 |
| 应用显示名 | CFBundleDisplayName |
桌面图标下方名称,建议 ≤ 12 字符 |
| 主 Storyboard | NSMainStoryboardFile |
主界面 storyboard 文件名 |
| 启动图 Storyboard | UILaunchStoryboardName |
启动屏 storyboard(iOS 8+ 推荐) |
| 本地化区域 | CFBundleDevelopmentRegion |
默认语言区域,如 zh_CN |
| 支持方向 | UISupportedInterfaceOrientations |
支持的屏幕方向 |
| 网络安全 | NSAppTransportSecurity |
HTTP 白名单配置(ATS) |
| 状态栏管理方式 | UIViewControllerBasedStatusBarAppearance |
YES 由 VC 管理,NO 由 UIApplication 管理 |
| URL Scheme 白名单 | LSApplicationQueriesSchemes |
iOS 9+ 跳转其他 App 前需声明 |
| 后台模式 | UIBackgroundModes |
音频、定位、蓝牙等后台运行权限 |
CFBundleIconFile是旧版图标设置方式,现代 iOS 使用 Asset Catalog(Assets.xcassets中的 AppIcon)配合CFBundleIcons键,不推荐再使用CFBundleIconFile。
3. PCH 文件
PCH(Precompiled Header)是预编译头文件,在编译时自动引入到每个 .m 文件中,用于存放公用宏和头文件。
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
// Debug 下输出 Log,Release 下不输出
#ifdef DEBUG
#define XMGLog(...) NSLog(__VA_ARGS__)
#else
#define XMGLog(...)
#endif
// iOS 版本判断(推荐用 @available 或 NSFoundationVersionNumber,浮点数比较有精度风险)
#define iOS8Later ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0)
#endif
现代使用说明:
- Xcode 6 及之后默认不再自动创建 PCH 文件,需手动添加
.pch文件并在Build Settings → Prefix Header中指定路径。 - PCH 会被每个源文件引入,内容过多会增加编译时间,现代开发推荐使用
@import模块导入或在具体文件中导入所需头文件,仅将真正全局通用的宏放入 PCH。 - iOS 版本判断推荐使用
@available(iOS 8.0, *)(编译期检查)或NSFoundationVersionNumber,避免浮点数比较的精度问题。
二、UIApplication
1. 什么是 UIApplication
UIApplication 是应用程序的核心单例对象,每个 App 有且仅有一个实例,通过 [UIApplication sharedApplication] 获取。它负责应用级别的操作,如打开 URL、管理状态栏、处理远程通知、协调应用生命周期事件等。
2. 常用属性和方法
应用图标角标
// 设置角标数字(iOS 16+ 需先请求通知权限)
[UIApplication sharedApplication].applicationIconBadgeNumber = 10;
网络活动指示器
// 显示状态栏网络活动小菊花
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
networkActivityIndicatorVisible在 iOS 13 后已废弃,刘海屏设备不再显示该指示器。
状态栏管理
iOS 7 起状态栏有两种管理方式:
方式一:由 UIViewController 管理(默认,推荐)
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent; // 白色文字
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
方式二:由 UIApplication 管理
需在 Info.plist 中设置 UIViewControllerBasedStatusBarAppearance = NO:
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
[UIApplication sharedApplication].statusBarHidden = NO;
iOS 13+ 中
UIApplication的statusBarStyle和statusBarHidden已废弃,推荐使用UIStatusBarManager(通过UIWindowScene获取)或继续使用 ViewController 方式。
打开 URL(打电话、发短信、网页等)
UIApplication *app = [UIApplication sharedApplication];
// 打电话(标准格式为 tel:,不是 tel://)
[app openURL:[NSURL URLWithString:@"tel:10086"]];
// 发短信
[app openURL:[NSURL URLWithString:@"sms:10086"]];
// 发邮件
[app openURL:[NSURL URLWithString:@"mailto:12345@qq.com"]];
// 打开网页
[app openURL:[NSURL URLWithString:@"https://www.baidu.com"]];
iOS 10+:
openURL:已废弃,推荐使用带完成回调的新 API:[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { NSLog(@"打开成功: %d", success); }];iOS 9+:通过
canOpenURL:检测能否打开某个 App 前,需在 Info.plist 的LSApplicationQueriesSchemes数组中声明目标 URL Scheme,否则返回 NO。
3. 单例模式
UIApplication 是标准单例,整个 App 生命周期内只有一个实例。以下是常见的单例实现方式:
// 推荐写法:dispatch_once 保证线程安全且只执行一次
+ (instancetype)sharedManager {
static Manager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
实际开发中通常不禁止
alloc(允许创建其他实例用于测试等场景),只提供sharedInstance作为默认共享实例。如果需要严格单例(禁止外部创建),可重写allocWithZone:并抛出异常,但这种做法在现代开发中较少使用。
三、UIApplicationDelegate
1. 作用
UIApplicationDelegate 协议定义了应用生命周期和系统事件的回调方法。UIApplication 单例在发生应用状态变化(启动、进入后台、内存警告等)时,通知 delegate 对象处理。
2. 核心生命周期方法
#pragma mark - 应用启动
// 应用启动完成,最常用的初始化入口
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
#pragma mark - 应用状态切换
// 应用即将从活跃状态变为非活跃(来电、下拉通知中心等)
- (void)applicationWillResignActive:(UIApplication *)application {}
// 应用进入后台(按 Home 键)
- (void)applicationDidEnterBackground:(UIApplication *)application {}
// 应用即将从后台进入前台
- (void)applicationWillEnterForeground:(UIApplication *)application {}
// 应用已进入活跃状态(可交互)
- (void)applicationDidBecomeActive:(UIApplication *)application {}
// 应用即将终止(非总是调用,如系统直接 kill 时不调用)
- (void)applicationWillTerminate:(UIApplication *)application {}
#pragma mark - 系统事件
// 收到内存警告,应释放可重建的资源
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {}
// 远程通知注册成功,获取 deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {}
// 打开 URL(如从其他 App 跳转回来)
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return YES;
}
3. AppDelegate 类
每个项目默认生成的 AppDelegate 类就是 UIApplication 的 delegate,遵守 UIApplicationDelegate 协议:
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 初始化窗口和根视图控制器
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
@end
4. iOS 13+ SceneDelegate 架构变化
iOS 13 引入 UIScene(场景)概念,支持多窗口和多场景。App 的 UI 生命周期管理从 AppDelegate 拆分到 SceneDelegate:
| 职责 | iOS 12 及之前 | iOS 13+ |
|---|---|---|
| App 生命周期 | AppDelegate | AppDelegate(仍负责 App 级别) |
| UI 生命周期 / UIWindow | AppDelegate | SceneDelegate |
| 窗口创建 | application:didFinishLaunchingWithOptions: |
scene:willConnectToSession:options: |
| 前后台切换 | AppDelegate 方法 | SceneDelegate 方法(sceneDidBecomeActive: 等) |
// SceneDelegate.h
#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
// SceneDelegate.m
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// 创建窗口(必须用 windowScene 初始化)
UIWindowScene *windowScene = (UIWindowScene *)scene;
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible];
}
- (void)sceneDidDisconnect:(UIScene *)scene {}
- (void)sceneDidBecomeActive:(UIScene *)scene {}
- (void)sceneWillResignActive:(UIScene *)scene {}
- (void)sceneWillEnterForeground:(UIScene *)scene {}
- (void)sceneDidEnterBackground:(UIScene *)scene {}
@end
Info.plist 中需配置
UIApplicationSceneManifest声明 SceneDelegate,否则仍使用旧的 AppDelegate 窗口管理方式。
四、UIWindow
1. 什么是 UIWindow
UIWindow 是 UIView 的子类,是 App 内容的顶层容器。一个 App 通常至少有一个 UIWindow,所有可见的视图都最终挂载在 window 上。iOS 13+ 多场景下,每个 UIWindowScene 对应一个或多个 window。
2. 添加内容到 UIWindow
方式一:设置 rootViewController(推荐)
window.rootViewController = yourViewController;
// rootViewController 的 view 会自动添加到 window,并由 VC 管理生命周期
方式二:直接 addSubview(不推荐)
[window addSubview:yourView];
// 直接添加 view 不会管理对应的 UIViewController 生命周期,不推荐用于主界面
3. 常用方法
// 成为 keyWindow(主窗口,接收键盘和非触摸事件)
[window makeKeyWindow];
// 成为 keyWindow 并显示(等价于 makeKeyWindow + hidden = NO)
[window makeKeyAndVisible];
iOS 15+ 中
makeKeyAndVisible被标记为不推荐但仍可用,推荐先设置windowScene再调用makeKeyWindow。
4. 获取 UIWindow
// 获取所有 window(iOS 15+ 中 windows 属性被标记为不推荐)
NSArray *windows = [UIApplication sharedApplication].windows;
// 获取 keyWindow(iOS 13+ 中 keyWindow 属性已废弃)
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
// 获取某个 view 所在的 window
UIWindow *viewWindow = yourView.window;
iOS 13+ 推荐获取 keyWindow 的方式:
UIWindow *keyWindow = nil;
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
for (UIWindow *window in scene.windows) {
if (window.isKeyWindow) {
keyWindow = window;
break;
}
}
if (keyWindow) break;
}
}
五、iOS 程序启动过程
1. main 函数与 UIApplicationMain
程序入口是 main.m 中的 main 函数,调用 UIApplicationMain:
int main(int argc, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
UIApplicationMain 函数签名:
int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
| 参数 | 说明 |
|---|---|
argc / argv |
命令行参数,透传即可 |
principalClassName |
UIApplication 类名(或子类),nil 默认为 UIApplication |
delegateClassName |
AppDelegate 类名,必须遵守 UIApplicationDelegate |
2. 完整启动流程
1. main() 函数执行
↓
2. 调用 UIApplicationMain()
↓
3. 创建 UIApplication 单例对象
↓
4. 创建并设置 UIApplication 的 delegate(AppDelegate)
↓
5. 启动主 RunLoop,开始事件循环
↓
6. 调用 delegate 的 application:didFinishLaunchingWithOptions:
↓
7. 创建 UIWindow(iOS 13+ 在 SceneDelegate 的 scene:willConnectToSession: 中创建)
↓
8. 设置 window.rootViewController
↓
9. [window makeKeyAndVisible],界面可见
↓
10. 调用 applicationDidBecomeActive:(或 sceneDidBecomeActive:),App 进入活跃状态
六、四大对象关系
| 对象 | 角色 | 关键职责 |
|---|---|---|
UIApplication |
应用单例 | 应用级别操作、协调生命周期、打开 URL、管理状态栏 |
UIApplicationDelegate |
应用代理 | 处理 App 生命周期事件、系统事件、远程通知、URL 打开 |
UIWindow |
顶层视图容器 | 承载所有可见视图、管理 keyWindow、分发触摸事件 |
UIViewController |
视图控制器 | 管理 view 层级、处理用户交互、控制视图生命周期 |
关系描述:
UIApplication单例持有delegate(AppDelegate),在应用状态变化时通知 delegate。- App 启动时创建
UIWindow,设置rootViewController,将 VC 的 view 添加到 window 上。 UIWindow继承自UIView,是视图层级的根,负责将触摸事件分发给子视图和视图控制器。- iOS 13+ 中,
UIWindow归属于UIWindowScene,SceneDelegate 管理场景级别的 UI 生命周期。
七、Swift 版本对照
AppDelegate(iOS 13+)
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
// 释放可重建资源
}
}
SceneDelegate(iOS 13+)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window
}
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillResignActive(_ scene: UIScene) {}
func sceneWillEnterForeground(_ scene: UIScene) {}
func sceneDidEnterBackground(_ scene: UIScene) {}
}
UIApplication 常用操作
// 角标
UIApplication.shared.applicationIconBadgeNumber = 10
// 打开 URL
if let url = URL(string: "tel:10086") {
UIApplication.shared.open(url, options: [:]) { success in
print("打开: \(success)")
}
}
// iOS 13+ 获取 keyWindow
let keyWindow = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }
单例
final class Manager {
static let shared = Manager() // 线程安全,懒加载
private init() {} // 禁止外部创建
}
八、总结
- Info.plist 是 App 的运行时配置核心,包含 Bundle ID、版本号、权限声明等,不能删除。常用键需熟悉,尤其是
CFBundleIdentifier、CFBundleShortVersionString、CFBundleVersion。 - UIApplication 是应用单例,负责应用级别操作。
openURL:在 iOS 10 后被openURL:options:completionHandler:替代;状态栏和网络指示器在 iOS 13+ 有废弃变化。 - UIApplicationDelegate 处理应用生命周期事件。iOS 13+ 引入 SceneDelegate,UI 窗口管理从 AppDelegate 拆分到 SceneDelegate。
- UIWindow 是顶层视图容器,推荐通过
rootViewController设置内容。iOS 13+ window 归属于UIWindowScene,keyWindow和windows属性在新版本中有废弃。 - 启动流程:
main → UIApplicationMain → 创建 UIApplication → 设置 delegate → 启动 RunLoop → didFinishLaunching → 创建 UIWindow → 设置 rootViewController → makeKeyAndVisible。 - PCH 文件现代开发中需谨慎使用,避免增加编译时间,推荐模块导入。

浙公网安备 33010602011771号