iOS|两张图帮你理清APP启动顺序以及试图生命周期

本文主要通过两张图来清晰明了的认识APP的启动顺序以及试图的生命周期,学习之前可以先了解下UIApplicationMain是如何帮助程序启动的。
废话不多说了,直接上两张图,下面两张图分别描绘了APP的启动顺序(App Launch Sequence)以及试图的生命周期(View Life Cycle):

iOS|两张图帮你理清APP启动顺序以及试图生命周期_第1张图片

从图中可以很清楚的看出 App 的启动顺序:

  1. main()呼叫UIApplicationMain()
  • UIApplicationMain()创造UIApplication
  • UIApplicationMain()创造AppDelegate
  • UIApplicationMain()载入Info.plist
  • UIApplication创造并管理RunLoop
  • UIApplication传送给application:didFinishLaunchingWithOptions:
  • application:didFinishLaunchingWithOptions:创造并显示Application Window
  • Application Window交给Root View Controller

注意:Xcode4.2之后启动顺序有所改变
本段参考自[iOS] APP啟動順序 (App Launch Sequence)

------------------------------我是小小分割线---------------------

iOS|两张图帮你理清APP启动顺序以及试图生命周期_第2张图片

ViewController的生命周期中各方法的执行顺序:
+ (void)load;
1.这是应用程序启动就会调用的方法,在这个方法里写的代码最先调用

+ (void)initialize;
2.这个是需要用到本类时才调用,这个方法里一般写 设置导航控制器的主题啊 之类的,如果在后面的方法设置导航栏主题就晚了!(当然在上面的方法里也能写)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
3.这个方法里面会创建UIWindow,设置根控制器并展现,比如某些应用程序要加载授权页面也是在这加,也可以设置观察者,监听到通知切换根控制器

ChildView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
4.childView的initwithcoder会在MainView的方法之前调用

MainView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
5.就是关于应用程序的数据存储后的解档操作。

MainView - (void)awakeFromNib;
6.在这个方法里设置view的背景等一系列普通操作,不要写关于frame的还不准,在使用IB的时候才会涉及到此方法的使用,当.nib文件被加载的时候,会发送一个awakeFromNib的消息到.nib文件中的每个对象,每个对象都可以定义自己的awakeFromNib函数来响应这个消息,执行一些必要的操作。

ChildView - (void)awakeFromNib
7.子控件也有本方法,重写父类的方法。基本用法同上

- (void)loadView;
8.创建视图的层次结构,这里需要注意,在没有创建控制器的view的情况下不能直接写 self.view 因为self.view的底层是:

if(_view == nil){

  _view = [self loadView]

}

所以这么写会直接造成死循环。

如果重写这个loadView方法里面什么都不写,会显示黑屏。

如果写了[super view]还要看前面的控制器在创建时是写的initWithNibName(指定了xib名字),还是写的普通的init。 如果是后者还是黑屏。

如果不在这个方法中,init的底层是会调用initWithNibName的,如果名字是MainViewController,会先在项目中找MainView.xib 找不到会再找MainViewController.xib。

- (void)viewDidLoad;
9.卧槽,这个方法是当年用的最多的方法,但是在之后的开发中就会发现越来越不靠谱,很多东西都还没加载完毕,各种取值都不准确,很少在这里面写东西了。 这里只是把视图元件加载完成,还没有开始布局不要设置关于 frame 之类的属性!有时可能会出现差20个像素点等状况。

- (void)viewWillAppear:(BOOL)animated;
10.视图将要出现,这个方法用的非常多,比如如果要设置导航栏的setNavigationBarHiden:animate: 就必须要在这里写,才能完美契合,不卡跳。 还有很多比如监听屏幕旋转啦,

viewWillTransitionToSize:可能要在本方法里再调一次,或者就是新到这个界面要reloadData 或是自动下拉刷新等 都是写在本方法里。

- (void)viewWillLayoutSubviews;
11.视图将要布局子视图,苹果建议的设置界面布局属性的方法,这个方法和viewWillAppear里,系统的底层都是没有写任何代码的,也就是说这里面不写super 也是可以的

MainView - (void)layoutSubviews;
12.在这个方法里一般设置子控件的frame,因为这里相当于是布局基本完成了,设置时取到的frame或者是self.bounds才最准,如果在awakeFromeNib里写会不准确 。还有这里要切记千万不能把super layoutSubviews忘了,可能最后都很难找到这个bug

- (void)viewDidLayoutSubviews;
13.这个方法我也是玩玩没想到,控制器的view的子控件还没有布局好呢,怎么这个控制器就已经说布局全部完成了?那后边的布局就不等了? 有独到见解的也恳请你告诉我,这其中苹果的意思到底是什么。

ChildView - (void)layoutSubviews;
14.控制器的子控件里的子控件的布局就在这里写了。

MainView - (void)drawRect:(CGRect)rect;
15.因为默认所有额UI控件都是画上去的,在这一步就是把所有的东西画上去,有时候需要用到Quartz2D的知识的时候都是在这个方法里话,但也是要注意别忘了写super,不然系统原本的东西就都画不上来了,这里要建议尽可能使用贝塞尔路径画图形,因为系统默认的那个上下文画法有时可能会内存泄露。drawRect方法只能在加载时调用一次,如果后面还需要调用,比如下载进度的圆弧,需要一直刷帧,就要使用setNeedsDisplay来定时多次调用本方法

ChildView - (void)drawRect:(CGRect)rect;
16.view的子控件内部的画图方法,有时可以自己自定义label 中间带个删除线的(用来写打折前的原价) 就是在这里画根线 。

- (void)viewDidAppear:(BOOL)animated;
17.把上面的画图都画完了,这里就会显示,视图完全加载完成。在这里的操作可能就是设置页面的一些动画,或者是设置tableView,collectionView,QQ聊天页面啥的滚动到底部scrollToIndexPath之类的代码操作。

- (void)applicationDidBecomeActive:(UIApplication *)application;
18.最后这是AppDelegate的应用程序获取焦点方法,真正到了这里,才是所有东西全部加载完毕,应用程序整装待发保持最佳状态等待用户操作。这个方法中一般会写关于弹出键盘的方法,比如有的用户登录界面为了更好的用户体验,就让你在刚打开程序来到登录界面的时候,光标的焦点就自动在账号的文本框里闪烁,也就是设置账号文本框为第一响应者。键盘在页面加载完毕后从下方弹出,这种代码一般就在本方法写。


  • 其他方法

- (void)viewDidAppear;
视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了

- (void)viewWillDisappear;
视图将被从屏幕上移除之前执行

- (void)viewDidDisappear;
视图已经被从屏幕上移除,用户看不到这个视图了

- (void)viewWillUnload;
在⾮非ARC当中.当前控制器的View即将被销毁的时候会调⽤用

- (void)viewDidUnload;

当系统内存吃紧的时候会调用该方法(注:viewController没有被dealloc)
内存吃紧时,在iPhone OS 3.0之前didReceiveMemoryWarning是释放无用内存的唯一方式,但是OS 3.0及以后viewDidUnload方法是更好的方式
在该方法中将所有IBOutlet(无论是property还是实例变量)置为nil(系统release view时已经将其release掉了)
在该方法中释放其他与view有关的对象、其他在运行时创建(但非系统必须)的对象、在viewDidLoad中被创建的对象、缓存数据等 release对象后,将对象置为nil(IBOutlet只需要将其置为nil,系统release view时已经将其release掉了)
一般认为viewDidUnload是viewDidLoad的镜像,因为当view被重新请求时,viewDidLoad还会重新被执行
viewDidUnload中被release的对象必须是很容易被重新创建的对象(比如在viewDidLoad或其他方法中创建的对象),不要release用户数据或其他很难被重新创建的对象

- (void)dealloc;
视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放
viewDidUnload和dealloc方法没有关联,dealloc还是继续做它该做的事情


ViewController是iOS开发中MVC模式中的C,ViewController是View的Controller(所以如此命名),ViewController的职责主要包括管理内部各个View的载入、显示和释放,同時负责与其它ViewController的沟通与协调。

在iOS中,有两类ViewController:

  • 一类是显示内容的ViewController::比如UIViewController、UITableViewController等,同时还可以自定义继承自UIViewController的ViewController。
  • 一类是ViewController容器:UINavigationViewController和UITabBarController等,UINavigationController是以Stack的形式来存储和管理ViewController,UITabBarController是以Array的形式来管理ViewController。

从图中可以看到,在View载入过程中。首先会调用loadView方法,在这个方法中主要完成一些关键View的初始化工作,比如UINavigationViewController和UITabBarController等容器类的ViewController;接下来就要载入View,成功载入后,接着会调用viewDidLoad方法,这里要记住的重点是,在loadView之前,是没有View的,换句话说在这之前,View还没有被初始化。完成viewDidload方法后,ViewController里面才会成功载入view。

在Controller中创建View有两种方式,一种是通过code产生、一种是通过Storyboard或Interface Builder来建立,后者有比较直观的配置View的外观和属性。

本段参考自[iOS] 視圖的生命週期 (View Life Cycle),对应用程序启动时所有方法的调用顺序分析

你可能感兴趣的:(iOS|两张图帮你理清APP启动顺序以及试图生命周期)