iOS程序入口结构
盛年不重来,一日难再晨。及时宜自勉,岁月不待人。
1. 程序入口
在我们开始开发app的时候,第一步往往是通过设置AppDelegate.m的代理方法开始写一些启动的东西,然后再通过控制器ViewController.m实现相应的布局。
// AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
// 实现一些布局
- (void)viewDidLoad;
而不会去过多关注程序的入口,真正的app程序入口是通过工程根目录下Supporting Files -> main.m
执行的main
函数。如下:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
2. UIApplicationMain函数
2.1)UIApplicationMain函数
UIApplicationMain(<#int argc#>, <#char * _Nullable * _Nonnull argv#>, NSString * _Nullable principalClassName, <#NSString * _Nullable delegateClassName#>)
四个参数:
argh
:代表的是长度;argv
:代表的是char 型数组,系统默认传进来的;
然后主要分析后面两个参数:
principalClassName
:UIApplication类或者其子类的类名,如果传 nil 默认是 UIApplication;delegateClassName:
UIApplication 的代理类的类名;
If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no.
NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.
UIApplicationMain方法定义,后面两个参数都是NSString类型的,根据参数字面意思都是类名,第一个默认传nil,那具体代表的是哪个类?我们先从最后一个参数看起,最后一个是一个代理类类名,即AppDelegate的类名,NSStringFromClass([AppDelegate class]等价于@“AppDelegate”,即把类名转换为字符串。AppDelegate这个是一个代理类,这个代理是实现的是谁的代理呢?查看AppDelegate.h发现是实现的UIApplication的代理,再根据苹果给出的注释来看,当这个类名为空时,先从Info.plist中读取NSPrincipalClass属性值,如果这个属性值不存在,则使用UIApplication类,说明最后两个参数一个是传UIApplication单例类,一个是实现UIApplication的代理AppDelegate,所以UIApplicationMain也可以改为UIApplicationMain(argc, argv, @"UIApplication", @"AppDelegate");其中第三个参数也可以是UIApplication类的子类。
2.2)UIApplicationMain死循环验证
main函数的返回值是一个int类型那么我们定义一个变量接收并打印,看看这个参数是什么,能不能打印?
int main(int argc, char * argv[]) {
@autoreleasepool {
int value = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"%d",value);
return value;
//return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
经过测试,日志是没有输出的,说明UIApplicationMain是个死循环。
我们再看一下UIApplicationMain死循环是什么?其实就是我们所说的runloop,那么内部开启死循环runloop的目的是什么?
保证当前线程(主线程)不被退出
负责监听事件(包括触摸事件、网络等等)
2.3)UIApplicationMain函数做了哪些工作和任务
UIApplicationMain函数中创建了一个UIApplication对象,每个iOS程序有且仅有一个UIApplication对象,此对象是单例,负责单例对象的维护和循环运行事件。程序一旦创建了UIApplication单例对象,对象就会一直循环下去。当应用程序在运行过程中,UIApplication对象会在应用出现变化时,调用不同的delegate方法发送特定的消息。
创建UIApplication对象:
从给定的类名初始化应用程序对象,也就是初始化UIApplication或者子类对象的一个实例,如果你在这里给定的是nil,那么系统会默认UIApplication类,也就主要是这个类来控制以及协调应用程序的运行。在后续的工作中,你可以用静态方法sharedApplication 来获取应用程序的句柄。
设置了代理:创建APPDelegate对象,并且成为UIApplication对象代理属性:
从给定的应用程序委托类,初始化一个应用程序委托。并把该委托设置为应用程序的委托,这里就有如果传入参数为nil,会调用函数访问 Info.plist文件来寻找主nib文件,获取应用程序委托。
启动主事件循环(Runloop)并开始接收事件;
加载info.plist文件(只读);
通过info.plist的key
Main storyboard file base name
寻找是否有指定的main.storyboard,有则进入main.storyboard;如果没有给值,则跳转至自己设置在
keyWindow
上ViewController
;如果没有给值,也没有在window上设置控制器,那么启动app失败,进入黑屏模式;
3. UIApplication实例职责
上面讲到UIApplicationMain
函数的工作,接下来一个问题是应用程序视图的显示、消息的控制怎么办?下面就是UIApplication
(或者子类)对象的职责,这个对象主要做下面几件事:
- 负责处理到来的用户事件,并分发事件消息到应该处理该消息的目标对象(sender, action)。
- 管理以及控制视图,包括呈现、控制行为、当前显示视图等。
- 该对象有一个应用程序委托对象,当一些生命周期内重要事件(可以包括系统事件或者生命周期控制事件)发生时,应用程序通知该对象。例如,应用程序启动、内存不够了或者应用程序结束等,让这些事件发生时,应用程序委托去响应。
通过上面的分析,可以知道UIApplication
对开发者来说,是一个黑箱。因为所有的操作,都可以由它的委托来帮我们完成,它只需要在后面维护一些不可更改的东西,如事件消息分发和传递、给委托发送事件处理请求等等,如,应用程序加载处理完毕,它会发送消息给委托,然后委托可以在 applicationDidFinishLanching
委托函数中去实现开发者想要的动作。利用Xcode在创建应用程序时,会默认实现一个应用程序委托类。而对于加载的视图,则有视图相关的委托类来处理视图加载过程的生命事件。下面介绍委托主要可以办哪些事情。
4. 代理类(AppDelegate)职责
(1)控制应用程序的行为(运行状态)
//程序即将启动完成
//对应未启动状态,告诉代理程序已经进入启动状态但是还没有进入未激活状态
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
return YES;
} //程序启动完成
//对应已经启动状态,准备进入前台开始运行状态,当没有接收到事件时则表示的是未激活状态
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
} //应用程序进入激活状态,应用程序可以接受事件并对其进行处
-(void)applicationDidBecomeActive:(UIApplication *)application{ } //应用程序放弃了活动状态进入未激活状态,在此状态中应用程序无法接受事件进行处理
-(void)applicationWillResignActive:(UIApplication *)application{ } //应用程序进入后台,在后台继续执行的代码在此可以进行处理即可
- (void)applicationDidEnterBackground:(UIApplication *)application { } //应用程序将要进入前台,包含两个状态未激活和激活状态
-(void)applicationWillEnterForeground:(UIApplication *)application{ } //程序将要终止退出,用来保存一些数据和转状态,以及应用程序退出前的内存清理工作
-(void)applicationWillTerminate:(UIApplication *)application{ } // 应用程序完成载入
-(void)applicationDidFinishLaunching:(UIApplication*)application{ }
(2)通知委托,应用程序收到了来自系统的内存不足警告
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application{}
(3)通知委托系统时间发生改变(主要是指时间属性,而不是具体的时间值)
-(void)applicationSignificantTimeChange:(UIApplication *)application{}
(4)打开URL
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{}
(5)控制状态栏方位变化
- (void)application:(UIApplication *)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration
(6)设备方向将要发生改变
- (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation{}
各种状态的委托对应的通知
UIKIT_EXTERN NSNotificationName const UIApplicationDidEnterBackgroundNotification NS_AVAILABLE_IOS(4_0);
UIKIT_EXTERN NSNotificationName const UIApplicationWillEnterForegroundNotification NS_AVAILABLE_IOS(4_0);
UIKIT_EXTERN NSNotificationName const UIApplicationDidFinishLaunchingNotification;
UIKIT_EXTERN NSNotificationName const UIApplicationDidBecomeActiveNotification;
UIKIT_EXTERN NSNotificationName const UIApplicationWillResignActiveNotification;
UIKIT_EXTERN NSNotificationName const UIApplicationDidReceiveMemoryWarningNotification;
UIKIT_EXTERN NSNotificationName const UIApplicationWillTerminateNotification;
UIKIT_EXTERN NSNotificationName const UIApplicationSignificantTimeChangeNotification;
因此,在UIApplication
中处理的系统事件时,只需转到delegate
这个类去处理, 这个类对象就是应用程序委托对象。我们可以从应用程序的单例类对象中得到应用程序委托的对象
UIApplicationDelegate* myDelegate = [[UIApplication sharedApplication] delegate];
UIApplication
接收到所有的系统事件和生命周期事件时,都会把事件传递给UIApplicationDelegate
进行处理,对于用户输入事件,则传递给相应的目标对象去处理。比如我们在应用程序被来电等消息后,可以调用应用程序委托类的 applicationWillResignActive()
方法,这个方法在用户锁住屏幕时,也会调用,与之相适应的是应用程序重新被用户打开时的委托方法。另外常用的就是内存不足的系统警告,此时会调用应用程序委托类的applicationDidReceiveMemoryWarning()
方法, 然后我们就可以试着释放一些内存了。
iOS 13的一大改进就是支持multiple windows(多窗口)功能,自从Xcode11发布以来,当你使用新XCode创建一个新的iOS项目时,SceneDelegate会被默认创建,iOS13 项目中的SceneDelegate类有什么作用?以及AppDelegate类的新变化,后面会专门说明。
iOS程序入口结构的更多相关文章
- 【Xamarin挖墙脚系列:Xamarin.IOS的程序的结构】
原文:[Xamarin挖墙脚系列:Xamarin.IOS的程序的结构] 开始熟悉Xamarin在开发IOS的结构!!!!!!! 先看官方 这个是以一个单页面的程序进行讲述的. 1 程序引用的程序集,核 ...
- [iOS] 使用xib作为应用程序入口 with IDE
[iOS] 使用xib作为应用程序入口 with IDE 在「使用xib做为应用程序入口 with Code」这篇文章中,介绍了如何透过写Code的方式,来使用xib做为应用程序的入口.但其实在Xco ...
- [iOS] 使用xib做为应用程序入口 with Code
[iOS] 使用xib做为应用程序入口 with Code 前言 开发iOS APP的时候,使用storyboard能够快速并且直觉的建立用户界面.但在多人团队开发的情景中,因为storyboard是 ...
- iOS开发系列--IOS程序开发概览
概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...
- 转:iOS程序main函数之前发生了什么
原文地址:http://blog.sunnyxx.com/2014/08/30/objc-pre-main/ 我是前言 一个iOS app的main()函数位于main.m中,这是我们熟知的程序入口. ...
- iOS程序main函数之前发生了什么
我是前言 一个iOS app的main()函数位于main.m中,这是我们熟知的程序入口.但对objc了解更多之后发现,程序在进入我们的main函数前已经执行了很多代码,比如熟知的+ load方法等. ...
- iOS程序猿如何快速掌握 PHP,化身"全栈攻城狮"?
这是一篇以 iOS 开发人员的视角写给广大iOS 程序猿的 PHP 入门指南.在这篇文章里我努力去发掘 objectiv-c 与 php 之间的共性,来帮助有一定 iOS 开发经验的攻城狮来快速上手一 ...
- 【转】漫谈iOS程序的证书和签名机制
转自:漫谈iOS程序的证书和签名机制 接触iOS开发半年,曾经也被这个主题坑的摸不着头脑,也在淘宝上买过企业证书签名这些服务,有大神都做了一个全自动的发布打包(不过此大神现在不卖企业证书了),甚是羡慕 ...
- 漫谈iOS程序的证书和签名机制
接触iOS开发半年,曾经也被这个主题坑的摸不着头脑,也在淘宝上买过企业证书签名这些服务,有大神都做了一个全自动的发布打包(不过此大神现在不卖企业证书了),甚是羡慕和崇拜.于是,花了一点时间去研究了一下 ...
- 深入浅出-iOS程序性能优化 (转载)
iOS应用是非常注重用户体验的,不光是要求界面设计合理美观,也要求各种UI的反应灵敏,我相信大家对那种一拖就卡卡卡的 TableView 应用没什么好印象. iOS应用是非常注重用户体验的,不光是要求 ...
随机推荐
- C++ 核心指南之 C++ 哲学/基本理念(下)
C++ 核心指南(C++ Core Guidelines)是由 Bjarne Stroustrup.Herb Sutter 等顶尖 C+ 专家创建的一份 C++ 指南.规则及最佳实践.旨在帮助大家正确 ...
- 华为ensp配置静态路由,三路由,三pc
华为ensp配置静态路由 目的:使pc1,pc2,pc3能相互ping通 1,tuop图的搭建 1,如图所示:先搭建好设备的通讯关系,在标记好每台设备对应的,ip地址和网关. 2,pc的网关,与ip地 ...
- 图解算法,原理逐步揭开「GitHub 热点速览」
想必每个面过大厂的小伙伴都被考过算法,那么有没有更快了解算法的方式呢?这是一个老项目,hello-algo 用图解的方式让你了解运行原理.此外,SQL 闯关自学项目也是一个让你能好好掌握 SQL 技术 ...
- frp内网穿透环境搭建--服务端ubuntu 客户端win10
前提条件:1个公网ip服务器,例如我的是腾讯云服务器ubuntu20 下载frp软件,下的是0.33.0版本,该版本直接把软件封装成服务,能用ubuntu直接定义开机自启等 github:https: ...
- Java将MySQL建表语句转换为SQLite的建表语句
Java将MySQL建表语句转换为SQLite的建表语句 源代码: package com.fxsen.platform.core.util; import java.util.HashMap; im ...
- ATtiny88初体验(四):看门狗
ATtiny88初体验(四):看门狗 ATtiny88单片机的看门狗使用内部独立的128KHz时钟源,拥有3种工作模式: Interrupt模式:超时产生中断: System Reset模式:超时产生 ...
- from my mac
hello
- ipmitool配置机器的BMC
一.设置IP地址 1.确定操作对象 #ipmitool mc info 输出中"Device Revision"是命令的操作对象 2.设置BMC IP # ipmitool -I ...
- SQL Server实例间同步登录用户
SQL Server实例间同步登录用户 问题痛点:由于AlwaysOn和数据库镜像无法同步数据库外实例对象,例如 登录用户.作业.链接服务器等,导致主库切换之后,应用连接不上数据库或者作业不存在导致每 ...
- KRPano最新官方文档中文版
KRPano最新官方文档中文版: KRPano作为VR全景开发中常用的工具软件,深受广大开发者喜爱,但由于软件本身是国外软件,因此官方的文档都是以英文为主,对于一些国内不太熟悉英文的开发者来说比较不友 ...