说明:此文是自己的总结笔记,主要参考:

iOS程序的启动执行顺序 AppDelegate 及 UIViewController 的生命周期

UIView的生命周期

言叶之庭.jpeg

一. iOS程序的启动执行顺序

程序启动顺序图

iOS启动原理图.png

具体执行流程

  1. 程序入口
    进入main函数,设置AppDelegate称为函数的代理

  2. 程序完成加载
    [AppDelegate application:didFinishLaunchingWithOptions:]

  3. 创建window窗口

  4. 程序被激活
    [AppDelegate applicationDidBecomeActive:]

  5. 当点击command+H时(针对模拟器,手机是当点击home键)
    程序取消激活状态
    [AppDelegate applicationWillResignActive:];
    程序进入后台
    [AppDelegate applicationDidEnterBackground:];

  6. 点击进入工程
    程序进入前台
    [AppDelegate applicationWillEnterForeground:]
    程序被激活
    [AppDelegate applicationDidBecomeActive:];

分析

1. 对于applicationWillResignActive(非活动)applicationDidEnterBackground(后台)这两个的区别。

  • applicationWillResignActive(非活动):
    比如当有电话进来或短信进来或锁屏等情况下,这时应用程序挂起进入非活动状态,也就是手机界面还是显示着你当前的应用程序的窗口,只不过被别的任务强制占用了,也可能是即将进入后台状态(因为要先进入非活动状态然后进入后台状态)

  • applicationDidEnterBackground(后台):
    指当前窗口不是你的App,大多数程序进入这个后台会在这个状态上停留一会,时间到之后会进入挂起状态(Suspended)。如果你程序特殊处理后可以长期处于后台状态也可以运行。
    Suspended (挂起): 程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。

如下图所示:

活动和非活动.png

2.UIApplicationMain 函数解释:

入口函数:

  1. int main(int argc, char * argv[]) {
  2. @autoreleasepool {
  3. return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  4. }
  5. }
  6. UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
  • argc argv 参数是为了与C语言保持一致。

  • principalClassName (主要类名) delegateClassName (委托类名)
    (1) 如果principalClassName是nil,那么它的值将从Info.plist去获取,如果Info.plist没有,则默认为UIApplicationprincipalClass这个类除了管理整个程序的生命周期之外什么都不做,它只负责监听事件然后交给delegateClass去做。
    (2) delegateClass 将在工程新建时实例化一个对象。NSStringFromClass([AppDelegate class])

  • AppDelegate 类实现文件

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  2. NSLog(@"--- %s ---",__func__);//__func__打印方法名
  3. return YES;
  4. }
  5. - (void)applicationWillResignActive:(UIApplication *)application {
  6. NSLog(@"--- %s ---",__func__);
  7. }
  8. - (void)applicationDidEnterBackground:(UIApplication *)application {
  9. NSLog(@"--- %s ---",__func__);
  10. }
  11. - (void)applicationWillEnterForeground:(UIApplication *)application {
  12. NSLog(@"--- %s ---",__func__);
  13. }
  14. - (void)applicationDidBecomeActive:(UIApplication *)application {
  15. NSLog(@"--- %s ---",__func__);
  16. }
  17. - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
  18. NSLog(@"--- %s ---",__func__);
  19. }
  20. - (void)applicationWillTerminate:(UIApplication *)application {
  21. NSLog(@"--- %s ---",__func__);
  22. }

打印调用顺序
启动程序

  1. --- -[AppDelegate application:didFinishLaunchingWithOptions:] ---
  2. --- -[AppDelegate applicationDidBecomeActive:] ---

按下 Command + H + SHIFT

  1. --- -[AppDelegate applicationWillResignActive:] ---
  2. --- -[AppDelegate applicationDidEnterBackground:] ---

重新点击 进入程序

  1. --- -[AppDelegate applicationWillEnterForeground:] ---
  2. --- -[AppDelegate applicationDidBecomeActive:] ---

选择 模拟器的Simulate Memory Warning

  1. --- -[AppDelegate applicationDidReceiveMemoryWarning:] ---

分析:

1.application:didFinishLaunchingWithOptions:
程序首次已经完成启动时执行,一般在这个函数里创建window对象,将程序内容通过window呈现给用户。

  1. applicationWillResignActive(非活动)
    程序将要失去Active状态时调用,比如有电话进来或者按下Home键,之后程序进入后台状态,对应的applicationWillEnterForeground(即将进入前台)方法。

    该函数里面主要执行操作:
    a . 暂停正在执行的任务
    b. 禁止计时器
    c. 减少OpenGL ES帧率
    d. 若为游戏应暂停游戏

3.applicationDidEnterBackground(已经进入后台)
对应applicationDidBecomeActive(已经变成前台)

该方法用来:
a. 释放共享资源
b. 保存用户数据(写到硬盘)
c. 作废计时器
d. 保存足够的程序状态以便下次修复;

  1. applicationWillEnterForeground(即将进入前台)
    程序即将进入前台时调用,对应applicationWillResignActive(即将进入后台)
    这个方法用来: 撤销applicationWillResignActive中做的改变。

  2. applicationDidBecomeActive(已经进入前台)
    程序已经变为Active(前台)时调用。对应applicationDidEnterBackground(已经进入后台)
    注意: 若程序之前在后台,在此方法内刷新用户界面

  3. applicationWillTerminate
    程序即将退出时调用。记得保存数据,如applicationDidEnterBackground方法一样。

等候.jpeg

二. UIViewController 的 生命周期

代码 示例

  1. #pragma mark --- life circle
  2. // 非storyBoard(xib或非xib)都走这个方法
  3. - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  4. NSLog(@"%s", __FUNCTION__);
  5. if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
  6. }
  7. return self;
  8. }
  9. // 如果连接了串联图storyBoard 走这个方法
  10. - (instancetype)initWithCoder:(NSCoder *)aDecoder {
  11. NSLog(@"%s", __FUNCTION__);
  12. if (self = [super initWithCoder:aDecoder]) {
  13. }
  14. return self;
  15. }
  16. // xib 加载 完成
  17. - (void)awakeFromNib {
  18. [super awakeFromNib];
  19. NSLog(@"%s", __FUNCTION__);
  20. }
  21. // 加载视图(默认从nib)
  22. - (void)loadView {
  23. NSLog(@"%s", __FUNCTION__);
  24. self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
  25. self.view.backgroundColor = [UIColor redColor];
  26. }
  27. //视图控制器中的视图加载完成,viewController自带的view加载完成
  28. - (void)viewDidLoad {
  29. NSLog(@"%s", __FUNCTION__);
  30. [super viewDidLoad];
  31. }
  32. //视图将要出现
  33. - (void)viewWillAppear:(BOOL)animated {
  34. NSLog(@"%s", __FUNCTION__);
  35. [super viewWillAppear:animated];
  36. }
  37. // view 即将布局其 Subviews
  38. - (void)viewWillLayoutSubviews {
  39. NSLog(@"%s", __FUNCTION__);
  40. [super viewWillLayoutSubviews];
  41. }
  42. // view 已经布局其 Subviews
  43. - (void)viewDidLayoutSubviews {
  44. NSLog(@"%s", __FUNCTION__);
  45. [super viewDidLayoutSubviews];
  46. }
  47. //视图已经出现
  48. - (void)viewDidAppear:(BOOL)animated {
  49. NSLog(@"%s", __FUNCTION__);
  50. [super viewDidAppear:animated];
  51. }
  52. //视图将要消失
  53. - (void)viewWillDisappear:(BOOL)animated {
  54. NSLog(@"%s", __FUNCTION__);
  55. [super viewWillDisappear:animated];
  56. }
  57. //视图已经消失
  58. - (void)viewDidDisappear:(BOOL)animated {
  59. NSLog(@"%s", __FUNCTION__);
  60. [super viewDidDisappear:animated];
  61. }
  62. //出现内存警告 //模拟内存警告:点击模拟器->hardware-> Simulate Memory Warning
  63. - (void)didReceiveMemoryWarning {
  64. NSLog(@"%s", __FUNCTION__);
  65. [super didReceiveMemoryWarning];
  66. }
  67. // 视图被销毁
  68. - (void)dealloc {
  69. NSLog(@"%s", __FUNCTION__);
  70. }

查看 打印 结果

  1. 2017-03-01 18:03:41.577 ViewControllerLifeCircle[32254:401790] -[ViewController initWithCoder:]
  2. 2017-03-01 18:03:41.579 ViewControllerLifeCircle[32254:401790] -[ViewController awakeFromNib]
  3. 2017-03-01 18:03:41.581 ViewControllerLifeCircle[32254:401790] -[ViewController loadView]
  4. 2017-03-01 18:03:46.485 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLoad]
  5. 2017-03-01 18:03:46.486 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillAppear:]
  6. 2017-03-01 18:03:46.487 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillLayoutSubviews]
  7. 2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLayoutSubviews]
  8. 2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewWillLayoutSubviews]
  9. 2017-03-01 18:03:46.488 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidLayoutSubviews]
  10. 2017-03-01 18:03:46.490 ViewControllerLifeCircle[32254:401790] -[ViewController viewDidAppear:]
  11. 2017-03-01 19:03:13.308 ViewControllerLifeCircle[32611:427962] -[ViewController viewWillDisappear:]
  12. 2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController viewDidDisappear:]
  13. 2017-03-01 19:03:14.683 ViewControllerLifeCircle[32611:427962] -[ViewController dealloc]
  14. 2017-03-01 19:12:05.927 ViewControllerLifeCircle[32611:427962] -[ViewController didReceiveMemoryWarning]

分析
1.initWithNibName:bundle:
初始化UIViewController,执行关键数据初始化操作,非StoryBoard创建UIViewController都会调用这个方法。
注意: 不要在这里做View相关操作,ViewloadView方法中才初始化。

2. initWithCoder:
如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewControllerStoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle不会被调用,但是initWithCoder会被调用。

3. awakeFromNib
awakeFromNib方法被调用时,所有视图的outletaction已经连接,但还没有被确定,这个方法可以算作适合视图控制器的实例化配合一起使用的,因为有些需要根据用户洗好来进行设置的内容,无法存在storyBoardxib中,所以可以在awakeFromNib方法中被加载进来。

4. loadView
当执行到loadView方法时,如果视图控制器是通过nib创建,那么视图控制器已经从nib文件中被解档并创建好了,接下来任务就是对view进行初始化。

loadView方法在UIViewController对象的view被访问且为空的时候调用。这是它与awakeFromNib方法的一个区别。
假设我们在处理内存警告时释放view属性:self.view = nil。因此loadView方法在视图控制器的生命周期内可能被调用多次。
loadView方法不应该直接被调用,而是由系统调用。它会加载或创建一个view并把它赋值给UIViewControllerview属性。

在创建view的过程中,首先会根据nibName去找对应的nib文件然后加载。如果nibName为空或找不到对应的nib文件,则会创建一个空视图(这种情况一般是纯代码)

注意:在重写loadView方法的时候,不要调用父类的方法。

5. viewDidLoad
loadViewview载入内存中,会进一步调用viewDidLoad方法来进行进一步设置。此时,视图层次已经放到内存中,通常,我们对于各种初始化数据的载入,初始设定、修改约束、移除视图等很多操作都可以这个方法中实现。

视图层次(view hierachy):因为每个视图都有自己的子视图,这个视图层次其实也可以理解为一颗树状的数据结构。而树的根节点,也就是根视图(root view),在UIViewController中以view属性。它可以看做是其他所有子视图的容器,也就是根节点。
6. viewWillAppear
系统在载入所有的数据后,将会在屏幕上显示视图,这时会先调用这个方法,通常我们会在这个方法对即将显示的视图做进一步的设置。比如,设置设备不同方向时该如何显示;设置状态栏方向、设置视图显示样式等。

另一方面,当APP有多个视图时,上下级视图切换是也会调用这个方法,如果在调入视图时,需要对数据做更新,就只能在这个方法内实现。

7. viewWillLayoutSubviews
view 即将布局其Subviews。 比如viewbounds改变了(例如:状态栏从不显示到显示,视图方向变化),要调整Subviews的位置,在调整之前要做的工作可以放在该方法中实现

8.viewDidLayoutSubviews
view已经布局其Subviews,这里可以放置调整完成之后需要做的工作。

9. viewDidAppear
在view被添加到视图层级中以及多视图,上下级视图切换时调用这个方法,在这里可以对正在显示的视图做进一步的设置。

10.viewWillDisappear
在视图切换时,当前视图在即将被移除、或被覆盖是,会调用该方法,此时还没有调用removeFromSuperview

11. viewDidDisappear
view已经消失或被覆盖,此时已经调用removeFromSuperView;

12. dealloc
视图被销毁,此次需要对你在initviewDidLoad中创建的对象进行释放。

13.didReceiveMemoryWarning
在内存足够的情况下,app```的视图通常会一直保存在内存中,但是如果内存不够,一些没有正在显示的viewController就会收到内存不足的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只会释放内存,并不会释放对象的所有权,所以通常我们需要在这里将不需要显示在内存中保留的对象释放它的所有权,将其指针置nil``。

三. 视图的生命历程

  • [ViewController initWithCoder:][ViewController initWithNibName:Bundle]: 首先从归档文件中加载UIViewController对象。即使是纯代码,也会把nil作为参数传给后者。
  • [UIView awakeFromNib]: 作为第一个方法的助手,方法处理一些额外的设置。
  • [ViewController loadView]: 创建或加载一个view并把它赋值给UIViewControllerview属性。
    -[ViewController viewDidLoad]: 此时整个视图层次(view hierarchy)已经放到内存中,可以移除一些视图,修改约束,加载数据等。
  • [ViewController viewWillAppear:]: 视图加载完成,并即将显示在屏幕上。还没设置动画,可以改变当前屏幕方向或状态栏的风格等。
  • [ViewController viewWillLayoutSubviews]即将开始子视图位置布局
  • [ViewController viewDidLayoutSubviews]用于通知视图的位置布局已经完成
  • [ViewController viewDidAppear:]视图已经展示在屏幕上,可以对视图做一些关于展示效果方面的修改。
  • [ViewController viewWillDisappear:]视图即将消失
  • [ViewController viewDidDisappear:]视图已经消失

    四: 总结:

  • 只有init系列的方法,如initWithNibName需要自己调用,其他方法如loadViewawakeFromNib则是系统自动调用。而viewWill/Did系列的方法则类似于回调和通知,也会被自动调用。

  • 纯代码写视图布局时需要注意,要手动调用loadView方法,而且不要调用父类的loadView方法。纯代码和用IB的区别仅存在于loadView方法及其之前,编程时需要注意的也就是loadView方法。

  • 除了initWithNibNameawakeFromNib方法是处理视图控制器外,其他方法都是处理视图。这两个方法在视图控制器的生命周期里只会调用一次。

转自:http://www.jianshu.com/p/d60b388b19f5

iOS程序执行顺序和UIViewController 的生命周期(整理)的更多相关文章

  1. iOS程序的执行顺序 和 UIViewController的生命周期

    iOS程序的执行顺序 1 进入程序的入口 进入main函数, 设置AppDelegate称为函数的代理 2  程序完成加载 -[AppDelegate application:didFinishLau ...

  2. UIViewController的生命周期及iOS程序执行顺序

    UIViewController的生命周期及iOS程序执行顺序     当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序1. alloc                         ...

  3. iOS程序执行顺序 AppDelegate及 UIViewController 的生命周期

    iOS程序的启动执行顺序 AppDelegate 及 UIViewController 的生命周期 iOS应用程序的状态切换很重要,而UIViewControler对于iOS这种MVC模式来说尤为重要 ...

  4. UIViewController的生命周期及iOS程序执行顺序 和ios6 处理内存警告

    当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序1. alloc                                   创建对象,分配空间2.init (initWithN ...

  5. QF——iOS程序运行原理(APP的生命周期)

    iOS程序的运行原理: main.m: 1. main.m 主函数是所有程序的入口函数. 2. 在main函数里是UIApplicationMain函数,开启了一个无限循环,以监听该应用. 该UIAp ...

  6. IOS第12天(3,UIViewController的生命周期)

    #import "HMViewController.h" @interface HMViewController () @property(nonatomic,strong)NSA ...

  7. [转]JAVA程序执行顺序,你了解了吗:JAVA中执行顺序,JAVA中赋值顺序

    本文主要介绍以下两块内容的执行顺序,熟悉的大虾可以直接飘过. 一.JAVA中执行顺序 静态块 块 构造器 父类构造器 二.JAVA中赋值顺序 静态块直接赋值 块直接赋值 父类继承的属性已赋值 静态变量 ...

  8. PHPWind 8.7中代码结构与程序执行顺序

    pw9在此不谈,他是完全重构的作品,是完全MVC下的体系.当然,其中很多东西在PW8.7下已经可见端倪. 主要代码结构 1. 以现代的观点,PW是多入口应用模式,程序根目录下的文件几乎都是入口: 2. ...

  9. Flex 程序执行顺序!

    Flex 执行加载过程会有几个概念:preloader, SystemManager, Flex Application! flex 界面初始化时,看到的 Loading 加载条,那是 flex 自动 ...

随机推荐

  1. Arduino-定义串口

    在一个老外写的代码中找到了一个非常好的定义串口的方法!   Arduino用下面这种方法定义串口可以方便的把协议应用的任意的端口,大大提高了代码的修改性和移植性.       以下是范例:       ...

  2. C盘压缩,电脑无法正常启动的解决方法?

    有时候,我们觉得电脑很卡,因此压缩磁盘来节约资源,前段时间,由于不小心将C盘压缩了,导致电脑无法正常启动,查了一些有关的资料,发现很多人都遇到过类似的问题,如果你不想重装系统的话,那么,现在我说一下我 ...

  3. Max answer(The Preliminary Contest for ICPC China Nanchang National Invitational)

    Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values ...

  4. Win10远程桌面连接树莓派3时出现错误:由于安全设置

    http://blog.csdn.net/qq_33259138/article/details/52143407 在远程其树莓派时的电脑时提示错误“客户端无法建立与远程计算机的连接,远程计算机可能不 ...

  5. skimage.io.imread vs caffe.io.load_image

    这两周在跑一个模型,我真的是被折腾的要崩溃了. 最后原因就是数据类型的问题,你说是不是应该管小黑屋啊. skimage.io.imread得到的是uint8的数据,而caffe.io.load_ima ...

  6. matlab vs联调

    vs 和matlab联调,最近真的把我搞挂了要. 首先,怎么进入联调呢.在vs里先设置一下. vs:tools->attach to process,选择matlab,注意此时matlab一定是 ...

  7. base64位 解码图片

    jar包是commons-codec.jar. pnuts //base64解码成图片 function gldBase64ToImage(imgStr,imgFilePath) { // 对字节数组 ...

  8. postgresql 免安装版使用

    免安装版 postgresql 使用 1.首先使用 initdb 初始化数据目录 initdb --pgdata=data --encoding=UTF8 --locale=C 2.启动 postgr ...

  9. java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容

    8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...

  10. 交换机基础配置之单交换机划分vlan

    我们以以上拓扑图为例 pc0的IP地址为:192.168.1.1 pc1的ip地址为:192.168.1.2 两台主机在同一网段,相互ping是能ping通的 我们的目的是在单交换机上划分两个vlan ...