iOS 控制器生命周期
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,例如改变状态栏的取向,类型。
该方法被调用意味着控制器将一定会显示。
在控制器生命周期中,该方法可能会被多次调用。
注意:
如果A控制器present到B控制器,B控制器dismiss的时候,A的viewWillAppear不会被调用
④,viewWillLayoutSubviews()
该方法在通知控制器将要布局 view 的子控件时调用。
每当视图的 bounds 改变,view 将调整其子控件位置。
该方法可重写以在 view 布局子控件前做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 未起作用。
在控制器生命周期中,该方法可能会被多次调用。
⑤,viewDidLayoutSubviews()
该方法在通知控制器已经布局 view 的子控件时调用。
该方法可重写以在 view 布局子控件后做出改变。
该方法的默认实现为空。
该方法调用时,AutoLayout 已经完成。
在控制器生命周期中,该方法可能会被多次调用。
⑥,viewDidAppear()
该方法在控制器 view 已经添加到视图层次时被调用。
重写该方法需要首先调用父类该方法。
该方法可重写以进行有关正在展示的视图操作。
在控制器生命周期中,该方法可能会被多次调用。
⑦,viewWillDisappear()
该方法在控制器 view 将要从视图层次移除时被调用。
类似 viewWillAppear()。
该方法可重写以提交变更,取消视图第一响应者状态。
⑧,viewDidDisappear()
该方法在控制器 view 已经从视图层次移除时被调用。
类似 viewDidAppear()
该方法可重写以清除或隐藏控件。
⑨,didReceiveMemoryWarning()
当内存预警时,该方法被调用。
不能直接手动调用该方法。
该方法可重写以释放资源、内存。
⑩,deinit
控制器销毁时(离开堆),调用该方法。
可以移除通知,调试循环测试
总结:
当屏幕旋转,view 的 bounds 改变,其内部的子控件也需要按照约束调整为新的位置,因此也调用了 viewWillLayoutSubviews() 和 viewDidLayoutSubviews()。
当在一个控制器内 Present 新的控制器,原先的控制器并不会销毁,但会消失,因此调用了viewWillDisappear和viewDidDisappear方法。
如果新的控制器 Dismiss,即清除自己,原先的控制器会再一次出现,因此调用了其中的viewWillAppear和viewDidAppear方法。
若 loadView() 没有加载 view,viewDidLoad() 会一直调用 loadView() 加载 view,因此构成了死循环,程序即卡死。原文
附加面试题: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方法会调用多次
作者:大宝的爱情
链接:https://www.jianshu.com/p/bd2197d5e547