对应用程序启动时所有方法的调用顺序分析

一个应用程序的启动过程要包括代理的创建,控制器的加载和控制器view的加载,这其中有很多关于生命周期的方法,每个方法都是有先后顺序的,如果调用顺序拿不准,或者某段代码写的方法不恰当,就会遇到各种奇葩问题。本文不怕麻烦的在几乎所有启动时要调用的方法里都用了 __FUNCTION__ 打印。结果还有有些地方出人意料的

如果你不是在董铂然博客园看到本文,请点击查看原文

首先回顾一下应用程序的启动过程

①.先加载Main函数

②.在Main函数里的 UIApplicationMain方法中,创建Application对象 创建Application的Delegate对象

③.创建主循环,代理对象开始监听事件

④.启动完毕会调用 didFinishLaunching方法,并在这个方法中创建UIWindow

⑤.设置UIWindow的根控制器是谁

⑥.如果有storyboard,会根据info.plist中找到应用程序的入口storyboard并加载箭头所指的控制器

⑦.显示窗口

本文考虑的时步骤3之后到步骤7结束时将要调用的方法

其中有AppDelegate,ViewController,MainView(控制器的View),ChildView(子控件的View)的18个方法

AppDelegate中的:

1.application:didFinishLaunchingWithOptions:

2.applicationDidBecomeActive:

ViewController中的:

3.loadView

4.viewDidLoad

5.load

6.initialize

7.viewWillAppear

8.viewWillLayoutSubviews

9.viewDidLayoutSubviews

10.viewDidAppear

MainView(控制器的View)中的:

11.initWithCoder(如果没有storyboard就会调用initWithFrame,这里两种方法视为一种)

12.awakeFromNib

13.layoutSubviews

14.drawRect

ChildView(子控件View)中的:

15.initWithCoder(如果没有storyboard就会调用initWithFrame,这里两种方法视为一种)

16.awakeFromNib

17.layoutSubviews

18.drawRect

 

那么问题来了,不往下看你可以把上面的十八个方法排个顺序么?

下面的图是Xcode6.3的beta2版

有时有变化也就是最后两个方法有点出入

我更倾向于Xcode 6.1 觉得更科学 下面就是对各个方法的整理

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

 

如果你不是在董铂然博客园看到本文,请点击查看原文

以上总结,有不同意见的欢迎交流,欢迎关注

时间: 2024-10-19 00:07:49

对应用程序启动时所有方法的调用顺序分析的相关文章

iOS APP启动时所有方法的调用顺序分析

一个应用程序的启动过程要包括代理的创建,控制器的加载和控制器view的加载,这其中有很多关于生命周期的方法,每个方法都是有先后顺序的,如果调用顺序拿不准,或者某段代码写的方法不恰当,就会遇到各种奇葩问题.本文不怕麻烦的在几乎所有启动时要调用的方法里都用了 __FUNCTION__ 打印.结果还有有些地方出人意料的 首先回顾一下应用程序的启动过程 ①.先加载Main函数 ②.在Main函数里的 UIApplicationMain方法中,创建Application对象 创建Application的D

Android应用程序启动时发生AndroidRuntime : ClassNotFoundException for Activity class的解决方法

在android应用程序启动时抛出下面异常导致启动失败:07-09 17:12:35.709: ERROR/AndroidRuntime(3866): Uncaught handler: thread main exiting due to uncaught exception 07-09 17:12:35.719: ERROR/AndroidRuntime(3866): java.lang.RuntimeException: Unable to instantiate activity Com

iOS 应用程序启动时要做什么

当您的应用程序启动(无论是在前台或后台),使用您的应用程序委托application:willFinishLaunchingWithOptions:和application:didFinishLaunchingWithOptions:方法来执行以下操作: 检查启动选项字典的内容,了解有关启动应用程序的原因,并做出相应的响应. 初始化您的应用程序的关键数据结构. 准备您的应用程序的窗口和视图以供显示: 在启动时,系统会自动加载您的应用程序的main Storyboard加载初始视图控制器. app

如何在ASP.NET Core程序启动时运行异步任务(2)

原文:Running async tasks on app startup in ASP.NET Core (Part 2) 作者:Andrew Lock 译者:Lamond Lu 在我的上一篇博客中,我介绍了如何在ASP.NET Core应用程序启动时运行一些一次性异步任务.本篇博客将继续讨论上一篇的内容,如果你还没有读过,我建议你先读一下前一篇. 在本篇博客中,我将展示上一篇博文中提出的"在Program.cs中手动运行异步任务"的实现方法.该实现会使用一些简单的接口和类来封装应用

java程序 启动时参数

iEMP34:/opt/version/lktest/b030/jre/jre_linux/bin # ./java -classpath . SysInfo Exception in thread "main" java.lang.NoClassDefFoundError: org/hyperic/sigar/SigarException at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.

SpringBoot程序启动时执行初始化代码

因项目集成了Redis缓存部分数据,需要在程序启动时将数据加载到Redis中,即初始化数据到Redis. 在SpringBoot项目下,即在容器初始化完毕后执行我们自己的初始化代码. 第一步:创建实现ApplicationListener接口的类 package com.stone; import com.stone.service.IPermissionService; import org.springframework.context.ApplicationListener; import

ElasticSearch 启动时加载 Analyzer 源码分析

ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档介绍.ElasticSearch6.3.2源码中相关类:AnalysisModule.AnalysisPlugin.AnalyzerProvider.各种Tokenizer类和它们对应的TokenizerFactory.另外还参考了一个具体的基于ElasticSearch采用HanLP进行中文分词的

Activity生命周期方法的调用顺序工程与测试日志

下面为测试activity的方法的执行顺序   工程与测试资源地址 android工程 AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.finalizetest"

Linux下查看进程(程序)启动时的环境变量

背景: 因最近试安装Linux下的jira,有一个中文插件安装后,一旦设置开机启动后,它是英文,而在终端再重新启动一次后呢,似乎插件生效,它又恢复为正常中文界面,我首先想这这涉及到一个环境变量的问题,因为在我设置开机启动的服务时用service jira start出现找不到一些变量,如:获取不到java的home目录,提示找不到,为此,我加上了java的环境变量开机启动Ok了,但是英文,我对英文认识少,但还是想用中文,否则插件就等于白安了. 为此,需要弄清这两者的环境变量的区别在哪儿,如何查看