App的生命周期

iOS程序的启动执行顺序

程序启动顺序图

iOS启动原理图

具体执行流程

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"--- %s ---",__func__);//__func__打印方法名
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
     NSLog(@"--- %s ---",__func__);
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
   NSLog(@"--- %s ---",__func__);
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
   NSLog(@"--- %s ---",__func__);
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
  NSLog(@"--- %s ---",__func__);
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
     NSLog(@"--- %s ---",__func__);
}

- (void)applicationWillTerminate:(UIApplication *)application {
    NSLog(@"--- %s ---",__func__);
}

启动 APP

-[AppDelegate application:didFinishLaunchingWithOptions:] 
-[AppDelegate applicationDidBecomeActive:] 

按下 Home

-[AppDelegate applicationWillResignActive:]
-[AppDelegate applicationDidEnterBackground:] 

返回 APP

-[AppDelegate applicationWillEnterForeground:]
-[AppDelegate applicationDidBecomeActive:]

内存警告

-[AppDelegate applicationDidReceiveMemoryWarning:]

注意: application:didFinishLaunchingWithOptions:App 首次启动时调用,一般在这个函数里创建 window 对象,初始化 App 可能用到的第三方 SDK 等。 applicationWillResignActive:App 将要进入后台时调用,比如有电话进来或者按下 Home 键。 该函数里面主要执行操作:

  • 暂停正在执行的任务
  • 停止计时器
  • 减少OpenGL ES帧率

applicationDidEnterBackground:App 已经进入后台,一般该方法用来:

  • 释放共享资源
  • 保存用户数据(写到硬盘)
  • 销毁计时器

applicationWillEnterForeground:App 即将进入前台,一般该方法用来撤销 applicationWillResignActive: 中做的改变。 applicationDidBecomeActive:App 已经进入前台,若 App 之前在后台,在此方法内刷新用户界面。

UIViewController 的生命周期

// 非storyBoard(xib或非xib)都走这个方法
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

    }
    return self;
}

// storyBoard走这个方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
     NSLog(@"%s", __FUNCTION__);
    if (self = [super initWithCoder:aDecoder]) {

    }
    return self;
}

// xib 加载 完成
- (void)awakeFromNib {
    [super awakeFromNib];
     NSLog(@"%s", __FUNCTION__);
}

// 加载视图(默认从nib)
- (void)loadView {
    NSLog(@"%s", __FUNCTION__);
    self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.view.backgroundColor = [UIColor redColor];
}

// 视图控制器中的视图加载完成,viewController自带的view加载完成
- (void)viewDidLoad {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidLoad];
}

// 视图将要出现
- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillAppear:animated];
}

// view 即将布局其 Subviews
- (void)viewWillLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillLayoutSubviews];
}

// view 已经布局其 Subviews
- (void)viewDidLayoutSubviews {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidLayoutSubviews];
}

// 视图已经出现
- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidAppear:animated];
}

// 视图将要消失
- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewWillDisappear:animated];
}

// 视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"%s", __FUNCTION__);
    [super viewDidDisappear:animated];
}

// 出现内存警告 
- (void)didReceiveMemoryWarning {
    NSLog(@"%s", __FUNCTION__);
    [super didReceiveMemoryWarning];
}

// 视图被销毁
- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}

分析

  • initWithNibName:bundle: 初始化UIViewController,执行关键数据初始化操作,非StoryBoard创建UIViewController都会调用这个方法。 注意: 不要在这里做View相关操作,ViewloadView方法中才初始化。
  • initWithCoder: 如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewControllerStoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle不会被调用,但是initWithCoder会被调用。
  • awakeFromNibawakeFromNib方法被调用时,所有视图的outletaction已经连接,但还没有被确定,这个方法可以算作适合视图控制器的实例化配合一起使用的,因为有些需要根据用户喜好来进行设置的内容,无法存在storyBoardxib中,所以可以在awakeFromNib方法中被加载进来。
  • loadView 当执行到loadView方法时,如果视图控制器是通过nib创建,那么视图控制器已经从nib文件中被解档并创建好了,接下来任务就是对view进行初始化。 loadView方法在UIViewController对象的view被访问且为空的时候调用。这是它与awakeFromNib方法的一个区别。 假设我们在处理内存警告时释放view属性:self.view = nil。因此loadView方法在视图控制器的生命周期内可能被调用多次。 loadView方法不应该直接被调用,而是由系统调用,它会加载或创建一个view并把它赋值给UIViewControllerview属性。 在创建view的过程中,首先会根据nibName去找对应的nib文件然后加载。如果nibName为空或找不到对应的nib文件,则会创建一个空视图(这种情况一般是纯代码) 注意:在重写loadView方法的时候,不要调用父类的方法。
  • viewDidLoadloadViewview载入内存中,会进一步调用viewDidLoad方法来进行进一步设置。此时,视图层次已经放到内存中,通常,我们对于各种初始化数据的载入,初始设定、修改约束、移除视图等很多操作都可以这个方法中实现。
  • viewWillAppear 系统在载入所有的数据后,将会在屏幕上显示视图,这时会先调用这个方法,通常我们会在这个方法对即将显示的视图做进一步的设置。比如,设置设备不同方向时该如何显示;设置状态栏方向、设置视图显示样式等。 另一方面,当APP有多个视图时,上下级视图切换是也会调用这个方法,如果在调入视图时,需要对数据做更新,就只能在这个方法内实现。
  • viewWillLayoutSubviews view即将布局其Subviews。 比如viewbounds改变了(例如:状态栏从不显示到显示,视图方向变化),要调整Subviews的位置,在调整之前要做的工作可以放在该方法中实现
  • viewDidLayoutSubviews view已经布局其Subviews,这里可以放置调整完成之后需要做的工作。
  • viewDidAppearview被添加到视图层级中以及多视图,上下级视图切换时调用这个方法,在这里可以对正在显示的视图做进一步的设置。
  • viewWillDisappear 在视图切换时,当前视图在即将被移除、或被覆盖是,会调用该方法,此时还没有调用removeFromSuperview
  • viewDidDisappear view已经消失或被覆盖,此时已经调用removeFromSuperView;
  • dealloc 视图被销毁,此次需要对你在initviewDidLoad中创建的对象进行释放。
  • didReceiveMemoryWarning 在内存足够的情况下,app的视图通常会一直保存在内存中,但是如果内存不够,一些没有正在显示的viewController就会收到内存不足的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只会释放内存,并不会释放对象的所有权,所以通常我们需要在这里将不需要显示在内存中保留的对象释放它的所有权,将其指针置nil。

UIView 的生命周期

- (instancetype)init {
    if (self = [super init]) {
        NSLog(@"%s",__func__);
    }
    return self;
}

// 通过代码创建控件就会调用这个方法
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        NSLog(@"%s",__func__);
    }
    return self;
}

// 通过storyboared或者xib中创建控件就会调用这个方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        NSLog(@"%s",__func__);
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    NSLog(@"%s",__func__);
}

// 如果在initWithFrame中添加子视图会调用两次
- (void)layoutSubviews {
    [super layoutSubviews];
    NSLog(@"%s",__func__);
}

- (void)didAddSubview:(UIView *)subview {
    [super didAddSubview:subview];
    NSLog(@"%s",__func__);
}

- (void)willRemoveSubview:(UIView *)subview {
    [super willRemoveSubview:subview];
    NSLog(@"%s",__func__);
}

- (void)willMoveToSuperview:(nullable UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];
    NSLog(@"%s",__func__);
}

- (void)didMoveToSuperview {
    [super didMoveToSuperview];
    NSLog(@"%s",__func__);
}

- (void)willMoveToWindow:(nullable UIWindow *)newWindow {
    [super willMoveToWindow:newWindow];
    NSLog(@"%s",__func__);
}

- (void)didMoveToWindow {
    [super didMoveToWindow];
    NSLog(@"%s",__func__);
}

- (void)removeFromSuperview {
    [super removeFromSuperview];
    NSLog(@"%s",__func__);
}

- (void)dealloc {
    NSLog(@"%s",__func__);
}

view 创建时

2017-11-06 10:35:12.347153+0800 iOSLife[7587:2353869] -[View initWithFrame:]
2017-11-06 10:35:12.347312+0800 iOSLife[7587:2353869] -[View init]
2017-11-06 10:35:12.347153+0800 iOSLife[7587:2353869] -[View willMoveToSuperview:]
2017-11-06 10:35:12.347312+0800 iOSLife[7587:2353869] -[View didMoveToSuperview]
2017-11-06 10:35:12.353483+0800 iOSLife[7587:2353869] -[View willMoveToWindow:]
2017-11-06 10:35:12.353644+0800 iOSLife[7587:2353869] -[View didMoveToWindow]
2017-11-06 10:35:12.363861+0800 iOSLife[7587:2353869] -[View layoutSubviews]

view 销毁时

2017-11-06 10:41:28.152448+0800 iOSLife[7607:2356750] -[View willMoveToWindow:]
2017-11-06 10:41:28.152693+0800 iOSLife[7607:2356750] -[View didMoveToWindow]
2017-11-06 10:41:28.155160+0800 iOSLife[7607:2356750] -[View willMoveToSuperview:]
2017-11-06 10:41:28.155281+0800 iOSLife[7607:2356750] -[View didMoveToSuperview]
2017-11-06 10:41:28.155336+0800 iOSLife[7607:2356750] -[View removeFromSuperview]
2017-11-06 10:41:28.155399+0800 iOSLife[7607:2356750] -[View dealloc]

注意: 可以看出上面方法中只会执行一次的方法有 removeFromSuperviewdealloc 两个方法,layoutSubviews 在子视图布局变动时会多次调用,所以可以在 removeFromSuperviewdealloc 这两个方法中执行释放内存等操作,比如移除观察者,定时器等。

view 添加子视图时

2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View initWithFrame:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View init]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView initWithFrame:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView init]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView willMoveToSuperview:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView didMoveToSuperview]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View didAddSubview:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View willMoveToSuperview:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View didMoveToSuperview]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View willMoveToWindow:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView willMoveToWindow:]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView didMoveToWindow]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View didMoveToWindow]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View layoutSubviews]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[View layoutSubviews]
2017-11-06 10:45:47.749310+0800 iOSLife[17198:14898063] -[TestView layoutSubviews]

注意:didAddSubview:willRemoveSubview: 需要有子视图才能执行。

此时再销毁该 view

2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View willMoveToWindow:]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView willMoveToWindow:]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView didMoveToWindow]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View didMoveToWindow]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View willMoveToSuperview:]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View didMoveToSuperview]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View removeFromSuperview]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View dealloc]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[View willRemoveSubview:]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView willMoveToSuperview:]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView didMoveToSuperview]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView removeFromSuperview]
2017-11-06 10:46:28.022473+0800 iOSLife[17198:14898063] -[TestView dealloc]

willRemoveSubview 是在 dealloc 后面执行的。如果有多个子视图,willRemoveSubview 会循环执行,直到移除所有子视图。

注意:

- (void)willMoveToSuperview:(nullable UIView *)newSuperview;
- (void)willMoveToWindow:(nullable UIWindow *)newWindow;

这俩个方法可以根据参数判断,nil 则为销毁,否则为创建;

- (void)didMoveToSuperview;
- (void)didMoveToWindow;

这个方法可以根据 self.superview 判断,nil 则为销毁,否则为创建。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏青玉伏案

iOS开发之新浪微博山寨版代码优化

  之前发表过一篇博客“IOS开发之新浪围脖”,在编写代码的时候太偏重功能的实现了,写完基本功能后看着代码有些别扭,特别是用到的四种cell的类,重复代码有点多...

2019
来自专栏進无尽的文章

实践-小细节 III

Title:nil message:nil 标题和信息都设为 nil 不能设置为 @“” 这样还是会出现空白的标题

942
来自专栏冰霜之地

Weex 是如何在 iOS 客户端上跑起来的

2016年4月21日,阿里巴巴在Qcon大会上宣布跨平台移动开发工具Weex开放内测邀请。Weex能够完美兼顾性能与动态性,让移动开发者通过简捷的前端语法写出N...

2893
来自专栏xx_Cc的学习总结专栏

iOS-控制器View的创建和生命周期

3266
来自专栏Rindew的iOS技术分享

iOS初步集成极光推送后你还要做这些事

3155
来自专栏一“技”之长

从AFNetworking源码分析到应用全解 原

    AFNetworking是iOS/OS开发中常用的一个第三方网络库,可以说它是目前最流行的网络库,但其代码结构其实并不复杂,也可以说非常简洁优美。在AF...

3363
来自专栏iOS开发攻城狮的集散地

Url Scheme实现APP间通信、分享

接下来就以我之前写的UIActivityViewController系统原生分享-仿简书分享和iOS开源小项目-WSL两个Demo为例,让我们看下怎么可以让UI...

2458
来自专栏MixLab科技+设计实验室

设计师编程指南之Sketch插件开发 7 GUI

往期文章索引: 1 / 入门基本概念、page的相关操作 2 / artboard 、NSFileManager 和 NSString 关于文件及文件夹的相关操...

3544
来自专栏Alice

iOS afnetworking最新版报错 没有AFHTTPRequestOperationManager类了

今天开了一个小项目   用的是pod   然后  安装好 Afnetworking之后   发现 AFHTTPRequestOperationManager  ...

4309
来自专栏ios 技术积累

iOS SDWebimage 源码阅读

简介 SDWebimage是 iOS 的图片加载框架。它支持从网络中下载且缓存图片,并设置图片到对应的 UIImageView 控件或者 UIButton 控...

1251

扫码关注云+社区