iOS 生命周期

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 生命周期相关的知识,希望对你有一定的参考价值。

空闲时间想总结下生命周期这块,新建一个test项目,先看一下AppDelegate类方法的调用,在所有的方法中加入

NSLog(@"--- %s ---",__func__);打印方法名

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    NSLog(@"--- %s ---",__func__);

    

    return YES;

}

 

 

- (void)applicationWillResignActive:(UIApplication *)application {

    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.

    NSLog(@"--- %s ---",__func__);

}

 

 

- (void)applicationDidEnterBackground:(UIApplication *)application {

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.

    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    NSLog(@"--- %s ---",__func__);

}

 

 

- (void)applicationWillEnterForeground:(UIApplication *)application {

    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

    NSLog(@"--- %s ---",__func__);

}

 

 

- (void)applicationDidBecomeActive:(UIApplication *)application {

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    NSLog(@"--- %s ---",__func__);

}

 

 

- (void)applicationWillTerminate:(UIApplication *)application {

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

    NSLog(@"--- %s ---",__func__);

}

通过程序启动,进入后台及从后台唤醒进入前台的操作,查看方法调用顺序:

 

程序刚启动时调用方法:

[AppDelegate application:didFinishLaunchingWithOptions:]

[AppDelegate applicationDidBecomeActive:]  //程序被激活

程序进入后台时调用方法:

[AppDelegate applicationWillResignActive:] //程序取消激活状态

[AppDelegate applicationDidEnterBackground:] //程序进入后台

从后台唤醒进入前台时调用方法:

[AppDelegate applicationWillEnterForeground:] //程序进入前台

[AppDelegate applicationDidBecomeActive:] //程序被激活

 

分析:

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

2.applicationWillResignActive(非活动)

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

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

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

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

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

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

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

 

ViewController的生命周期:

创建两个VC,一个用SB创建,一个纯代码创建

- (void)viewDidLoad {

    [super viewDidLoad];

    self.title = @"第二页(SB)";

    self.view.backgroundColor = [UIColor whiteColor];

    NSLog(@"%s", __func__);

    // Do any additional setup after loading the view, typically from a nib.

}

-(instancetype)init{

    

    self = [super init];

    

    NSLog(@"%s", __func__);

    

    return self;

}

 

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    NSLog(@"%s", __func__);

    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

        

    }

    return self;

}

// 如果连接了串联图storyBoard 走这个方法

- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    NSLog(@"%s", __func__);

    if (self = [super initWithCoder:aDecoder]) {

        

    }

    return self;

}

 

// xib 加载 完成

- (void)awakeFromNib {

    [super awakeFromNib];

    NSLog(@"%s", __func__);

}

// 加载视图(默认从nib)

- (void)loadView {

    NSLog(@"%s", __func__);

    self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];

    self.view.backgroundColor = [UIColor redColor];

}

//视图将要出现

- (void)viewWillAppear:(BOOL)animated {

    NSLog(@"%s", __func__);

    [super viewWillAppear:animated];

}

// view 即将布局其 Subviews

- (void)viewWillLayoutSubviews {

    NSLog(@"%s", __func__);

    [super viewWillLayoutSubviews];

}

 

// view 已经布局其 Subviews

- (void)viewDidLayoutSubviews {

    NSLog(@"%s", __func__);

    [super viewDidLayoutSubviews];

}

 

//视图已经出现

- (void)viewDidAppear:(BOOL)animated {

    NSLog(@"%s", __func__);

    [super viewDidAppear:animated];

}

 

//视图将要消失

- (void)viewWillDisappear:(BOOL)animated {

    NSLog(@"%s", __func__);

    [super viewWillDisappear:animated];

}

 

//视图已经消失

- (void)viewDidDisappear:(BOOL)animated {

    NSLog(@"%s", __func__);

    [super viewDidDisappear:animated];

}

 

 

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    NSLog(@"%s", __func__);

    // Dispose of any resources that can be recreated.

}

根据打印结果可以看出方法的执行顺序,SB创建的:

2018-03-26 13:46:48.268882+0800 test[4659:680728] -[SecondViewController initWithCoder:]

2018-03-26 13:46:48.269376+0800 test[4659:680728] -[SecondViewController awakeFromNib]

2018-03-26 13:46:48.270668+0800 test[4659:680728] -[SecondViewController loadView]

2018-03-26 13:46:48.271432+0800 test[4659:680728] -[SecondViewController viewDidLoad]

2018-03-26 13:46:48.271676+0800 test[4659:680728] -[SecondViewController viewWillAppear:]

2018-03-26 13:46:48.281261+0800 test[4659:680728] -[SecondViewController viewWillLayoutSubviews]

2018-03-26 13:46:48.281423+0800 test[4659:680728] -[SecondViewController viewDidLayoutSubviews]

2018-03-26 13:46:48.783592+0800 test[4659:680728] -[SecondViewController viewDidAppear:]

 

纯代码创建的打印顺序为:

2018-03-26 13:47:19.564963+0800 test[4659:680728] -[ThirdViewController initWithNibName:bundle:]

2018-03-26 13:47:19.565357+0800 test[4659:680728] -[ThirdViewController init]

2018-03-26 13:47:19.566861+0800 test[4659:680728] -[ThirdViewController loadView]

2018-03-26 13:47:19.567676+0800 test[4659:680728] -[ThirdViewController viewDidLoad]

2018-03-26 13:47:19.568003+0800 test[4659:680728] -[ThirdViewController viewWillAppear:]

2018-03-26 13:47:19.580727+0800 test[4659:680728] -[ThirdViewController viewWillLayoutSubviews]

2018-03-26 13:47:19.580972+0800 test[4659:680728] -[ThirdViewController viewDidLayoutSubviews]

2018-03-26 13:47:20.084203+0800 test[4659:680728] -[ThirdViewController viewDidAppear:]



** 分析 **
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:]:视图已经消失
  • [ViewController dealloc:]:视图销毁的时候调用


参考链接:https://www.jianshu.com/p/d60b388b19f5

以上是关于iOS 生命周期的主要内容,如果未能解决你的问题,请参考以下文章

Android片段生命周期:onResume调用了两次

在不存在的片段上调用片段生命周期和 onCreate 的问题

导航上的片段生命周期重叠

Android 片段生命周期

关于片段生命周期,何时调用片段的 onActivityResult?

理解片段事务期间片段的生命周期方法调用