重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
1,单个viewController的生命周期
成都创新互联公司"三网合一"的企业建站思路。企业可建设拥有电脑版、微信版、手机版的企业网站。实现跨屏营销,产品发布一步更新,电脑网络+移动网络一网打尽,满足企业的营销需求!成都创新互联公司具备承接各种类型的成都网站制作、网站设计、外贸网站建设项目的能力。经过十载的努力的开拓,为不同行业的企事业单位提供了优质的服务,并获得了客户的一致好评。
①,initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
②,loadView:加载view
③,viewDidLoad:view加载完毕
④,viewWillAppear:控制器的view将要显示
⑤,viewWillLayoutSubviews:控制器的view将要布局子控件
⑥,viewDidLayoutSubviews:控制器的view布局子控件完成
这期间系统可能会多次调用viewWillLayoutSubviews 、 viewDidLayoutSubviews 俩个方法
⑦,viewDidAppear:控制器的view完全显示
⑧,viewWillDisappear:控制器的view即将消失的时候
这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
⑨,viewDidDisappear:控制器的view完全消失的时候
⑩,didReceiveMemoryWarning(内存满时)
当程序发出一个内存警告---
系统询问控制器有View吗---如果有View
系统询问这个View能够销毁吗----通过判断View是否在Windown上面,如果不在,就表示可以销毁
如果可以销毁,就执行viewWillUnLoad()-----对你的View进行一次release,此时View就为nil
然后调用viewDidUnLoad()-----一般还会在这个方法里将一些不需要属性清空
2,多个viewControllers跳转
当我们点击push的时候首先会加载下一个界面然后才会调用界面的消失方法
initWithCoder:(NSCoder *)aDecoder:ViewController2(如果用xib创建的情况下)
loadView:ViewController2
viewDidLoad:ViewController2
viewWillDisappear:ViewController1将要消失
viewWillAppear:ViewController2将要出现
viewWillLayoutSubviewsViewController2
viewDidLayoutSubviewsViewController2
viewWillLayoutSubviews:ViewController1
viewDidLayoutSubviews:ViewController1
viewDidDisappear:ViewController1完全消失
viewDidAppear:ViewController2完全出现
3,相关解释
①,loadView()
若控制器有关联的 Nib 文件,该方法会从 Nib 文件中加载 view;如果没有,则创建空白 UIView 对象。
自定义实现不应该再调用父类的该方法。
②,viewDidLoad()
view 被加载到内存后调用viewDidLoad()。
重写该方法需要首先调用父类该方法。
该方法中可以额外初始化控件,例如添加子控件,添加约束。
该方法被调用意味着控制器有可能(并非一定)在未来会显示。
在控制器生命周期中,该方法只会被调用一次。
③,viewWillAppear()
该方法在控制器 view 即将添加到视图层次时以及展示 view 时所有动画配置前被调用。
重写该方法需要首先调用父类该方法。
该方法中可以进行操作即将显示的 view,例如改变状态栏的取向,类型。
该方法被调用意味着控制器将一定会显示。
在控制器生命周期中,该方法可能会被多次调用。
注意:******
ViewControllerA present一个ViewControllerB,如果是iOS13 默认UIModalPresentationAutomatic样式,当ViewControllerB dismiss 到 ViewControllerA的时候,不调用ViewControllerA里面的,viewWillAppear方法和viewDidAppear方法。
modalPresentationStyle设置成UIModalPresentationFullScreen的模式,会调用viewWillAppear方法和viewDidAppear方法。
④,viewWillLayoutSubviews()
该方法在通知控制器将要布局 view 的子控件时调用。
每当视图的 bounds 改变,view 将调整其子控件位置。
该方法可重写以在 view 布局子控件前做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 未起作用。
在控制器生命周期中,该方法可能会被多次调用。
⑤,viewDidLayoutSubviews()
该方法在通知控制器已经布局 view 的子控件时调用。
该方法可重写以在 view 布局子控件后做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 已经完成。
在控制器生命周期中,该方法可能会被多次调用。
⑥,viewDidAppear()
该方法在控制器 view 已经添加到视图层次时被调用。
重写该方法需要首先调用父类该方法。
该方法可重写以进行有关正在展示的视图操作。
在控制器生命周期中,该方法可能会被多次调用。
⑦,viewWillDisappear()
该方法在控制器 view 将要从视图层次移除时被调用。
类似 viewWillAppear()。
该方法可重写以提交变更,取消视图第一响应者状态。
⑧,viewDidDisappear()
该方法在控制器 view 已经从视图层次移除时被调用。
类似 viewDidAppear()
该方法可重写以清除或隐藏控件。
⑨,didReceiveMemoryWarning()
当内存预警时,该方法被调用。
不能直接手动调用该方法。
该方法可重写以释放资源、内存。
⑩,deinit
控制器销毁时(离开堆),调用该方法。
可以移除通知,调试循环测试
总结:
当屏幕旋转,view 的 bounds 改变,其内部的子控件也需要按照约束调整为新的位置,因此也调用了 viewWillLayoutSubviews() 和 viewDidLayoutSubviews()。
ViewControllerA present一个ViewControllerB,如果是iOS13 默认UIModalPresentationAutomatic样式,当ViewControllerB dismiss 到 ViewControllerA的时候,不调用ViewControllerA里面的,viewWillAppear方法和viewDidAppear方法。
modalPresentationStyle设置成UIModalPresentationFullScreen的模式,会调用viewWillAppear方法和viewDidAppear方法。
若 loadView() 没有加载 view,viewDidLoad() 会一直调用 loadView() 加载 view,因此构成了死循环,程序即卡死。 原文
上面说的有些混乱了,下边看一下转场A viewwilldisappear,AviewDidDisappear和 B viewwillappear和viewdidappear的各种情况的出现顺序:
Push: A-willDisappear--B-willAppear--A-didDisappear--B-didAppear
fullScreenPresent: A-willDisappear--B-willAppear--B-didAppear--A-didDisappear
OtherPresent: B-willAppear--B-didAppear(因为A没有消失)
TabBar: B-willAppear--A-willDisappear--A-didDisappear--B-didAppear
在每一种转场下,appear 与 disappear 都有一些不一样的顺序,一定要分清楚,不能一概而论。
以下第一个出现的都是will,第二个都是did,will永远在did之前,因为一个是将要,一个是已经
push:abab(最正常)
fullScreenPresent:abba
otherPresent:bb(a不消失,所以不调用)
tabbar:baab
附加面试题:load和initialize的区别
******:load是只要类所在的文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会调用load方法,即使类文件被引用进来,如果没有使用,那么initialize不会被调用。
调用方式
1、load是根据函数地址直接调用
2、initialize是通过objc_msgSend调用
调用时刻
1、load是runtime加载类、分类的时候调用(只会调用一次)
2、initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次(如果子类没有实现initialize方法, 会调用父类的initialize方法, 所以父类的initialize方法可能会调用多次)
load和initializee的调用顺序
1、load:
先调用类的load, 在调用分类的load
先编译的类, 优先调用load, 调用子类的load之前, 会先调用父类的load
先编译的分类, 优先调用load
2、initialize
先初始化分类, 后初始化子类
通过消息机制调用, 当子类没有initialize方法时, 会调用父类的initialize方法, 所以父类的initialize方法会调用多次
链接:
当我们打开 APP 时,程序一般都是从 main 函数开始运行的,那么我们先来看下 Xcode 自动生成的 main.m 文件:
这个默认的 iOS 程序就是从 main 函数开始执行的,但是在 main 函数中我们其实只能看到一个方法,这个方法内部是一个消息循环(相当于一个死循环),因此运行到这个方法 UIApplicationMain 之后程序不会自动退出,而只有当用户手动关闭程序这个循环才结束。我们看下这个方法定义:
这个方法有四个参数:
关于返回值,即便声明了返回值,但该函数也从不会返回。
也就是说当执行 UIApplicationMain 方法后这个方法会根据第三个参数 principalClassName 创建对应的 UIApplication 对象,这个对象会根据第四个参数 delegateClassName 创建 AppDelegate 并指定此对象为 UIApplication 的代理;同时 UIApplication 会开启一个消息循环不断监听应用程序的各个活动,当应用程序生命周期发生改变 UIApplication 就会调用代理对应的方法。
既然应用程序 UIApplication 是通过代理和外部交互的,那么我们就有必要清楚 AppDelegate 的操作细节,在这个类中定义了生命周期的各个事件的执行方法:
简要说下我们不同的操作,程序运行结果:
通过简单的操作,大家对整个运行周期有了个大概的了解。再附上一张图,让大家有个清晰的认识:
总览 UIViewController 生命周期:
下面创建了一个 TestViewController 类,了解下整个过程:
TestViewController.m:
在 ViewController.m 中:
我们在创建 TestViewController 实例时,可以通过以下两种方法:
我们经常使用的是第二种创建方法,其实第二种方法默认实现了第一种的方法,只不过两个参数默认传的是 nil。
当 TestVeiwController 通过 xib 加载的时候,看下 viewDidLoad 之前发生了什么:
无 xib:
TestVeiwController 通过 storyboard 加载:
控制台输出:
我们可以看到通过 storyboard 实例化与 init 实例化在 loadView 方法调用之前走的是不同的方法。我们看下这几个方法的不同:
此方法发生在 nib 加载之前。
调用此方法进行 Controller 初始化,与 nib 加载无关。nib 的加载是懒加载,当 Controller 需要加载其视图时,才会加载此方法中指定的 nib。
可以看出该方法初始化的 Controller 不是从 nib 创建的。
此方法发生在 nib 加载期间。
所有 archived 对象的初始化使用此方法。nib 中存储的对象就是 archived 对象,所以此方法是 nib 加载对象时使用的初始化方法。
当从 nib 创建 UIViewController 时使用此方法。
此方法发生在 nib 中所有对象都已完全加载完之后。
如果 initWithCoder 是 unarchiving 开始,那此方法就是结束。
在此方法中创建视图。
我们可以通过下图来理解它的逻辑:
每次访问 view 时,就会调用 self.view 的 get 方法,在 get 方法中判断 self.view==nil ,不为 nil 就直接返回 view,等于 nil 就去调用 loadView 方法。loadView 方法会去判断有无指定 storyBord/Xib 文件,如果有就去加载 storyBord/Xib 描述的控制器 view,如果没有则系统默认创建一个空的 view,赋给 self.view。loadView 方法有可能被多次调用(每当访问 self.view 并且为 nil 时就会调用一次);
系统会自动为我们加载 view,我们完全没必要手动创建 view。
视图将要被展示的时候调用。
其调用的时机与视图所在层次有关。例如我们常用的 push 与 present 操作改变了当前视图层次,都会触发此方法。
1、那么 UIAlertController 也是 present 操作怎么没有触发呢?
因为 UIAlertController 在另一个 window 上,view 在自己所在的 window 中层次并没有改变,所以不会触发,同理在锁屏以及进入后台时也不会触发。
2、如果控制器 B 被展示在另一个控制器 A 的 popover 中,那么被展示的控制器 B 在消失后,控制器 A 并不会调用此方法。
官方原文:
例如我们使用的 addSubview 方法,如下:
AViewController.m 中:
当我们将 BViewController 从 AViewController 中移除后,并不会触发 AViewController 的 viewWillAppear 方法。
视图渲染完成后调用,与 viewWillAppear 配套使用。
这两个方法发生在 viewWillAppear 与 viewDidAppear 之间。
viewWillDisappear 与 viewDidDisappear 配套使用。
两个方法的调用时机同 viewWillAppear 和 viewDidAppear 道理相同。
这两个方法是收到内存警告时调用的。
在 iOS5 以及之前使用的方法,iOS6 及之后已经废弃。在收到内存警告时,在此方法中将 view 置为 nil;
收到内存警告时,系统自动调用此方法,回收占用大量内存的视图数据。我们一般不需要在这里做额外的操作。如果要自己处理一些额外内存,重写时需要调用父类方法,即 [super didReceiveMemoryWarning] 。
UIViewController 释放时调用此方法。UIViewController 的生命周期到此结束。
当我们重写此方法时,ARC 环境下不需要调用父类方法,MRC 环境下需要调用父类方法,即 [super dealloc] 。
本篇主要介绍了 APP 的生命周期,以及 UIViewController 的生命周期,对我们程序开发的过程有了更清晰的认识。
参考资料:
applicationDidFinishLaunching 和 didFinishLaunchingWithOptions 这两个都是App完成启动的函数,当两者都写时,执行后者
启动(未运行 - 激活) :
willFinishLaunchingWithOptions
didFinishLaunchingWithOptions
DidBecomeActive
进入后台(激活 - 后台) :
WillResignActive 进入后台调用瞬间
DidEnterBackground 已经完全进入后台,iPhone回到桌面
进入前台(后台/多任务 - 激活) :
WillEnterForeground App激活瞬间的展示动画
DidBecomeActive 已经完全进入前台,App的画面完全展示
进入多任务再进入前台(激活 - 多任务 - 激活) :
WillResignActive 激活 - 多任务
DidBecomeActive 多任务 - 激活
PS 注意这种情况下,从多任务到激活状态,不会调用WillEnterForeground,因为此时App是还在前台的,虽然画面是多任务
杀死App(多任务 - 终止) :
DidEnterBackground
WillTerminate