在iPhone应用程序里, main函数功能被最小化了。大部分实际工作是在UIApplicationMain 函数中完成的。 当你在Xcode中开始一个新的应用程序项目时,每个项目模版都提供了一个标准main函数实现如同在 “Handling Critical Application Tasks.”里的那个。Main函数只做了三件事: 创建了一个自释放池(autorelease pool),调用UIApplicationMain,然后释放autorelease pool。 除了很少的特例,你不应该修改它。
Listing 1-1 iPhone应用程序的main函数
#import <UIKit/UIKit.h> int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]int retVal = UIApplicationMain(argc, argv, nil, nil)[pool release]return retVal}
注意: 自释放池用在内存管理中。它是一个Cocoa机制用来延迟在一个函数体内创建的对象的释放。更多信息参见Memory Management Programming Guide for Cocoa。对于和自释放池相关的iPhone应用灶雹薯程序特定的内存管理指南,参见“Allocating Memory Wisely.”
上述列表中的中心部分UIApplicationMain函数采用了4个参数并使用它们来初始化应用程序。尽管你没必要更改传入参数的默认值,还是值得解释一下它们起动应用程序时的用途。除了argc 和 argv 参数外,这个函数采用两个字符串参数来识肆隐别基本类(也就是,应用程序对象类)和应用程序代理类。如果这个基本类字符串为空,隐者UIKit使用UIApplication类作为缺省值。 如果应用程序代理类为空,UIKit假设它为从你的应用程序主nib文件加载的对象其中之一(对于使用Xcode模版创建的应用程序)。 设置这些参数任意一个为非空数值将导致UIApplicationMain 函数在应用程序启动时创建一个相应类实例并为声明的目的使用它。这样,如果你的应用程序使用一个自定义的UIApplication子类(不推荐这样,但是当然是可能的),你将在第三个参数中指定你自定义类的名字。
当我们打开 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 的生命周期,对我们程序开发的过程有了更清晰的认识。
参考资料:
https://www.cnblogs.com/kenshincui/p/3890880.html
https://www.quora.com/Cocoa-API-What-is-the-difference-between-initWithCoder-initWithNibName-and-awakeFromNib-1
dyld全名为 dynamic loader (动态链接器)(默认路径是/usr/lib/搭春dyld)。当一个iOS应用程序启动吵枝改时,系统会先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境,开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类结构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到了熟悉的升判程序入口。
查看dyld源码
2.检查共享缓存是否已经映射到共享区域
3.加载system frameWork
4.加载所有插入的dylib
5.链接主程序
6.通过link函数链接所有插入的库,执行符号替换
7.进行初始化 *** 作
8.寻找主程序入口并执行
链接: https://www.jianshu.com/p/73a99303cd91
链接: https://www.jianshu.com/p/43db6b0aab8e
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)