iOS APP程序启动原理
UIApplication 程序启动原理
一个应用程序运行就必须要有一个进程,一个进程至少要有一个线程,我们把这个线程叫做主线程,主线程开启之后会开启一个主运行循环,如果不开启一个运行循环,程序开启了就马上结束了,不会一直运行。换句话来说一个APP开启就关闭了,所以必须开启一个死循环,不断的在监听事件,用户操作....等等一些事件,说完那就来看看程序怎么启动的。
一个app用程序启动就必须要有且仅有一个UIApplication(或则其子类)应用管理类,UIApplication 这个类的对象是一个单例对象。在程序里可用 [UIApplication sharedApplication]来获取这个单例对象,从这句话有能看出苹果单例的命名是 shared 开头
--------- 单例:就是不管声明多少次只有一个对象,换句话就是只有一份内存,
那么UIApplication对象何时创建的?
一个程序都有一个main函数,iOS也不例外在xcode里有一个main.m文件,这个main就是一个应用程序启动的入口。
这个方法会调用了 UIApplicationMain,一切奥秘都在这个方法里,我们先看看这个方法是怎么调用的
/// argc 与 argv是在终端传参的时候有用
// argc: 传入的参数,默认是1,如果在终端中调用当前应用程序传入其他参数就是其他参数个数 + 1
// argv: 默认第一个参数是程序的路径,其他参数是传进来的参数
// principalClassName: 默认就是@"UIApplication"
// delegateClassName: 利用类名转字符串不容易写错 传进去一个字符串利用反射机制把字符串反射成UIApplication的代理对象
UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
UIApplicationMain底层实现,主要是用到了反射机制利用字符串生成类创建对象
- 1. 首先会根据principalClassName的参数 去创建一个 Application 单例对象
- 2. 创建完Application对象接着会根据 delegateClassName 参数,创建一个Application的代理对象,并且指定Application的代理就是这个代理对象
- 3. 启动一个主运行循环(能够处理事件、监听用户操作的一个死循环) 监听系统事件,调用application对应代理方法
- 4. 加载info.plist文件:设置一些程序信息,并且判断有没用指定main.storyboard,加载storboard
- 初始化一个窗口
- 创建控制器,设置窗口根控制器
- 显示主窗口
上面提到一个UIApplication类,那么UIApplication对象有什么用?
UIApplication的一个主要工作是处理用户事件,它会起一个队列,把所有用户事件都放入队列,逐个处理,在处理的时候,它会发送当前事件到一个合适的处理事件的目标控件。此外,UIApplication实例还维护一个在本应用中打开的window列表(UIWindow实例),这样它就可以接触应用中的任何一个UIView对象。UIApplication实例会被赋予一个代理对象,以处理应用程序的生命周期事件,还可以做其他应用级别的任务。所以UIApplication的核心作用是提供了iOS程序运行期间的控制和协作工作。
一、 下面是利用UIApplication 设置一些应用的功能
1. 发送短信
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@"sms://466453"]];
2. APP图标消息数量提醒框
// 设置APP图标提醒数字,IOS8以后要想设置applicationIconBadgeNumber这个属性,
// 必须注册一个用户通知 -[UIApplication registerUserNotificationSettings:]
UIApplication *app = [UIApplication sharedApplication];
// 创建一个用户通知
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
// 注册通知
[app registerUserNotificationSettings:settings];
app.applicationIconBadgeNumber = ;
3. 网络状态设置
app.networkActivityIndicatorVisible = YES;
- 4.控制状态栏
———————— 第一种方法 交给UIViewController控制器控制管理
- IOS7应用程序的状态栏默认是交给UIViewController控制器控制管理
- 如果要交给Application管理需要在info.plist文件增加一条属性
// 带有动画效果的隐藏
// [app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
[app setStatusBarHidden:YES];
———————— 第二中方法 通过控制器去重写方法prefersStatusBarHidden方法完成隐藏状态栏
// 控制器 - 隐藏状态栏
- (BOOL)prefersStatusBarHidden
{ return YES; } // 控制器 - 状态栏的样式
- (UIStatusBarStyle)preferredStatusBarStyle
{ return UIStatusBarStyleLightContent; }
- 5.设置摇动手势的时候,是否支持redo,undo操作
[UIApplication sharedApplication].applicationSupportsShakeToEdit = YES;
- 6.判断程序运行状态
/// UIApplicationStateActive,
/// UIApplicationStateInactive,
/// UIApplicationStateBackground
if([UIApplication sharedApplication].applicationState == UIApplicationStateInactive){
NSLog(@"程序在运行状态");
}
- 7.阻止屏幕变暗进入休眠状态 -> 尽量别使用本功能,因为很耗电。
[UIApplication sharedApplication].idleTimerDisabled = YES;
- 8.在map上显示一个地址
NSString *addressText = @"1 Infinite Loop, Cupertino, CA 95014";
addressText = [addressTextstringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString *urlText = [NSStringstringWithFormat:@"http://maps.google.com/maps?q=%@", addressText];
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:urlText]];
- 9.发送电子邮件
NSString *recipients =@"mailto:first@example.com?cc=second@example.com,third@example.com&subject=Hello from California!";
NSString *body =@"&body=It is raining in sunny California!";
NSString *email = [NSStringstringWithFormat:@"%@%@", recipients, body];
email = [emailstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:email]];
- 10.打电话到一个号码
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:@"tel://10086"]];
- 11.打开一个网址
[[UIApplication sharedApplication] openURL:[NSURLURLWithString:@"http://www.baidu.com"]];
二、 UIApplication 与 delegate
上面介绍程序启动的时候在main函数里会调用UIApplicationMain方法传进去的后2个参数就是UIApplication 与UIApplicationDelegate 2个类的字符串,之后利用反射把UIApplication的实例对象设置为UIApplication的代理,所以在Xcode创建之后会带2个文件【AppDelegate.h】和【AppDelegate.m】这2个文件,这2个文件就是系统生成的UIApplication代理文件,里面遵守了【<UIApplicationDelegate>】这个协议,所以他能帮助UIApplication在程序启动的时候能监听很多事,比如
- - 应用程序的生命周期
- - 内存警告
- - 系统事件
- - 等等......
下面就是UIApplication代理能做的一些常用事件
// app程序完成后调用
. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 我们一般在这里做一些程序启动之后需要的设置
// 或者纯代码编写的时候,对窗口的初始化,控制器的指定......... return YES;
} // 当程序载入后执行
. - (void)applicationDidFinishLaunching:(UIApplication*)application // app失去焦点的时候
. - (void)applicationWillResignActive:(UIApplication *)application; // 返回后台调用
// app被打断的时候,在这里可以保存一些数据
. - (void)applicationDidEnterBackground:(UIApplication *)application; // 进入前台的时候调用
. - (void)applicationWillEnterForeground:(UIApplication *)application; // 应用程序获取到焦点的时候调用,
// 换句话来说就是回到前台能与用户交互的时候调用
. - (void)applicationDidBecomeActive:(UIApplication *)application; // 被销毁的时候调用
// 几乎感受这个事件的调用,因为事件太短了,所以如果程序关闭需要做些事情最好别在这里写,可能没等执行程序就关闭了
// 这个需要要设置UIApplicationExitsOnSuspend的键值
. - (void)applicationWillTerminate:(UIApplication *)application; // 内存警告调,因为iPhone内存有限,所以内存管理非常重要,不慎重程序内存越积越多,达到一定度,程序就会闪退,崩溃,其他问题,所以请珍惜每一份内存的使用,如果内存达到一定程度,你可以利用这个函数清理内存
. - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application; // 请求委托打开一个URL资源
// 当通过url执行
. - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url;
. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; // 当系统时间发生改变时执行
. - (void)applicationSignificantTimeChange:(UIApplication *)application; // 设置 StatusBar 状态
// 当StatusBar框方向将要变化时执行
// 当StatusBar框将要变化时执行
. - (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame;
// 当StatusBar框方向将要变化时执行
. - (void)application:(UIApplication *)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration;
// 当StatusBar框方向改变时执行
// 当StatusBar框变化完成后执行
. - (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame;
// 当StatusBar框方向变化完成后执行
. - (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation; // 当一个应用程序成功的注册一个推送服务(APS) 发送到代理去
. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
// 当一个应用程序注册一个推送服务(APS) 发送到代理中失败时执行
. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; // 当一个运行着的应用程序收到一个远程的通知 发送到代理去...
. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
// 当一个运行着的程序接受一个本地的通知时执行
. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification; // 通知代理,受保护的文件当前变为不可用的
. - (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application;
// 通知代理,受保护的文件当前变为可用的
. - (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application;
上面这些代理方法,就是AppDelegate需要帮助UIApplication做的事
三、接下来说说 UIWindow 窗口
UIWindow 一个特殊的UIView,也是继承自UIView。
ios程序启动之后第一个创建的控件就是UIWindow,之后在创建控制器的View,然后在把控制器的View加载到UIWindow上,然后显示,所以其实我们看到的界面都是显示在UIWindow上的。
界面显示的3个重要对象
- UIScreen: 连接设备 ——app与设备屏幕连接点
- UIWindow: 给屏幕提供绘制、显示的接口,屏幕上的所有的对象都是绘制上去的,没有窗口就不能显示东西
- UIView:提供一些绘图操作,绘制完毕在绘制到UIWindow上显示
storyboard加载过程
如果在info.plist文件里声明了storyboard显示项,程序会自动将指定的storboard加载并显示箭头指向的控制器
如果没有指向,其实在大项目中很少有直接指定某个storyboard显示项的都是在程序启动的时候手动创建,也就是UIApplication的代理方法里去手动加载storyboard,显示指定控制器。下面就是模仿系统加载 storyboard 的过程
1. 初始化一个窗口
注意:
- 如果窗口不被创建之后就被销毁就必须强引用,所以需要用一个成员属性强引用住,其实系统有一个自己创建的window,所以不用自己手动创建直接用系统的就行
- 必须要设置尺寸
- 如果给系统的self.window赋值,系统会直接把这个window添加到application.windows里面
// 窗口的层级关系 (往下层级越高)
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERNconst UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar;
UIWindow *window = [[UIWindow all] initWithFrame:[UIScreen main].bounds];
2. 加载main.storyboard,并且创建指定的控制器
// 加载Mainstoryboard
UIStoryboard *stotyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// 获取storyboard箭头指向的控制器
// UIViewController *vc = [stotyboard instantiateViewControllerWithIdentifier:@"ViewController"]; // 获取Storyboard ID 为ViewController
UIViewController *vc = [stotyboard instantiateInitialViewController];
// 设置根控制器
window.rootViewController = vc;
// 有人会想把控制器的view直接添加到window上,那样会有很多弊端的,所以别那样弄
- 例如程序旋转的时候view不会跟着一起旋转
3. 把创建的控制器作为窗口的根控制器,显示窗口
// 设置application的主窗口
// 这个方法内部会把window.hidden设置为YES
[window makeKeyAndVisible];
总结:
storyboard加载过程其实就3步骤
- 初始化一个窗口
- 加载main.storyboard,并且创建指定的控制器
- 把创建的控制器作为窗口的根控制器,显示窗口
最后我们来个总结:
程序启动首先去main.m文件去执行main方法 ———> 接着会执行UIApplicationMain的方法 ————> 【在里面会把传进的后面2个【一个UIApplication,一个是AppDelegate】参数实例,
并且设置AppDelegate 作为 UIApplication 的代理,然后启动一个主运行循环,监听事件,其他操作】
iOS APP程序启动原理的更多相关文章
- iOS app 程序启动原理
iOS app 程序启动原理 Info.plist: 常见设置 建立一个工程后,会在Supporting files文件夹下看到一个"工程名-Info.plist"的文件, ...
- iOS开发 - App程序启动原理
Info.plist和pch文件的作用 建立一个project后,会在Supporting files目录下看到一个"project名-Info.plist"的文件,该文件对pro ...
- iOS程序启动原理---iOS-Apple苹果官方文档翻译
本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址 //转载请注明出处--本文永久链接:http://www.cnblogs.com/Ch ...
- iOS -程序启动原理和UIApplication的介绍
一.UIApplication 简介 (1)UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序. (2)每一个Application都有自 ...
- iOS App签名的原理
前言 相信很多同学对于iOS的真机调试,App的打包发布等过程中的各种证书.Provisioning Profile. CertificateSigningRequest.p12的概念是模糊的,导致在 ...
- 【腾讯Bugly干货分享】iOS App 签名的原理
本文来自 WeRead 团队博客: http://wereadteam.github.io/ iOS 签名机制挺复杂,各种证书,Provisioning Profile,entitlements,Ce ...
- iOS App的启动过程
一.mach-O Executable 可执行文件 Dylib 动态库 Bundle 无法被连接的动态库,只能通过 dlopen() 加载 Image 指的是 Executable,Dylib 或者 ...
- 怎样做一个iOS App的启动分层引导动画?
一. 为什么要写这篇文章? 这是一个很古老的话题,从两年前新浪微博开始使用多层动画制作iOS App的启动引导页让人眼前一亮(当然,微博是不是历史第一个这个问题值得商榷)之后,各种类型的引导页层出不穷 ...
- iOS开发——UI进阶篇(七)程序启动原理、打电话、发短信
一.Info.plist常见的设置 1.建立一个工程后,会在Supporting files文件夹下看到一个“工程名-Info.plist”的文件,该文件对工程做一些运行期的配置,非常重要,不能删除 ...
随机推荐
- ZOJ1937:Addition Chains——题解
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1937 题目大意:创造一个数列,使得它: 1.单调不递减. 2.其中一个元素 ...
- TYVJ2032 升降梯上
Description: 开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道.一辆停在轨道底部的电梯.和电梯内一杆控制电梯升降的巨大手柄.Nescafe ...
- ipython 安装和更新
pip install ipython pip install --upgrade ipython pip install --upgrade pip 不管是用pip装什么模块,前面都尽量不要加sud ...
- 【图论-最短路】【P3393】逃离僵尸岛
传送门 Description 小a住的国家被僵尸侵略了!小a打算逃离到该国唯一的国际空港逃出这个国家. 该国有N个城市,城市之间有道路相连.一共有M条双向道路.保证没有自环和重边. K个城市已经被僵 ...
- JavaScript定义类的方式与其它OO语言有些差异
JavaScript面向对象的程序编写与其它OO语言有一些出入,所以使用JavaScript的面向对象特性的时候,需要注意一些规范性的问题.下面就简单地谈一下,JavaScript如何定义一个类,在定 ...
- pg_basebackup: invalid tar block header size
问题: 在使用pg_basebackup搭建备节点时,由于pg_basebackup本身使用的是int整型来保存传输的数据大小,当传输的数据大于4G的话,整数就会溢出,进而报出:pg_baseback ...
- 在IAR使用FreeRTOS出现Error[Pa045]: function "XXX" has no prototype
FreeRTOS官方例程中设置了需要“Require prototype”,所以每个函数(除了main函数)都需要函数声明,其中对于无形参的函数声明要加void,比如void led_init(voi ...
- robots.txt使用和优化技巧
一.利于网站优化的robots.txt使用技巧 1.在线建站提供方便之路.当我们将域名解析到服务器,可以访问站点了,可是这个时候站点还没有布局好,meta标签还一塌糊涂.乳沟此时的站点被 搜索引擎蜘蛛 ...
- Update SSM agent to each EC2 via Bat and bash script
1. copy the instance id from aws console to file 2. remove the , from file sed -i 's/,//g' file 3. g ...
- [Leetcode] LRU 算法实现
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...