APP启动顺序
参考:
http://blog.sunnyxx.com/2014/08/30/objc-pre-main/
//www.greatytc.com/p/5efe327ac7ea
一、main函数之前
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。
1、系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境。
2、开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。
3、当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类机构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到程序入口main函数。
解释:
1、Mach-O文件格式是OS X与iOS系统上的可执行文件格式,像我们编译过程产生的.O文件,以及程序的可执行文件,动态库等都是Mach-O文件,
2、动态链接库:iOS 中用到的所有系统framework都是动态链接的,类比成插头和插排,静态链接的代码在编译后的静态链接过程就将插头和插排一个个插好,运行时直接执行二进制文件;而动态链接需要在程序启动时有需要再去完成插好相关的插头和插排,所以在我们写的代码执行前,动态连接器需要完成准备工作。
二、从main函数开始
1、main函数是ios程序的入口点,内部会调用UIApplicationMain函数,这个函数会创建一个UIApplication对象
2、然后创建UIApplication的delegate对象 —–AppDelegate ,开启一个消息循环(main runloop),每当监听到对应的系统事件时,就会通知AppDelegate。
解释:
1、UIApplication对象是应用程序的象征,每一个应用都有自己的UIApplication对象,而且是单例的
2、main函数解释
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
第一个参数表示参数的个数,第二个参数表示装载函数的数组,第三个参数,是UIApplication类名或其子类名,若是nil,则默认使用UIApplication类名。第四个参数是协议UIApplicationDelegate的实例化对象名,这个对象就是UIApplication对象监听到系统变化的时候通知其执行的相应方法
3、启动完毕会调用 didFinishLaunching方法,并在这个方法中创建UIWindow,设置AppDelegate的window属性,并设置UIWindow的根控制器。如果有storyboard,会根据info.plist中找到应用程序的入口storyboard并加载箭头所指的控制器,显示窗口。storyboard和xib最大的不同在于storyboard是基于试图控制器的,而非视图或窗口。展示之前会将添加rootViewController的view到UIWindow上面(在这一步才会创建控制器的view)
三、UIViewController 的 生命周期
2017-03-01 18:03:41.577 -[ViewController initWithCoder:]
2017-03-01 18:03:41.579 -[ViewController awakeFromNib]
2017-03-01 18:03:41.581 -[ViewController loadView]
2017-03-01 18:03:46.485 -[ViewController viewDidLoad]
2017-03-01 18:03:46.486 -[ViewController viewWillAppear:]
2017-03-01 18:03:46.487 -[ViewController viewWillLayoutSubviews]
2017-03-01 18:03:46.488 -[ViewController viewDidLayoutSubviews]
2017-03-01 18:03:46.488 -[ViewController viewWillLayoutSubviews]
2017-03-01 18:03:46.488 -[ViewController viewDidLayoutSubviews]
2017-03-01 18:03:46.490 -[ViewController viewDidAppear:]
2017-03-01 19:03:13.308 -[ViewController viewWillDisappear:]
2017-03-01 19:03:14.683 -[ViewController viewDidDisappear:]
2017-03-01 19:03:14.683 -[ViewController dealloc]
2017-03-01 19:12:05.927 -[ViewController didReceiveMemoryWarning]
1.initWithNibName:bundle:
初始化UIViewController,执行关键数据初始化操作,非StoryBoard创建UIViewController都会调用这个方法。
注意: 不要在这里做View相关操作,View在loadView方法中才初始化。
2.initWithCoder:
如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewController,StoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle不会被调用,但是initWithCoder会被调用。
3.awakeFromNib
当awakeFromNib方法被调用时,所有视图的outlet和action已经连接,但还没有被确定,这个方法可以算作适合视图控制器的实例化配合一起使用的,因为有些需要根据用户喜好来进行设置的内容,无法存在storyBoard或xib中,所以可以在awakeFromNib方法中被加载进来。
4.loadView
当执行到loadView方法时,如果视图控制器是通过nib创建,那么视图控制器已经从nib文件中被解档并创建好了,接下来任务就是对view进行初始化。
loadView方法在UIViewController对象的view被访问且为空的时候调用。这是它与awakeFromNib方法的一个区别。
假设我们在处理内存警告时释放view属性:self.view = nil。因此loadView方法在视图控制器的生命周期内可能被调用多次。
loadView方法不应该直接被调用,而是由系统调用。它会加载或创建一个view并把它赋值给UIViewController的view属性。
在创建view的过程中,首先会根据nibName去找对应的nib文件然后加载。如果nibName为空或找不到对应的nib文件,则会创建一个空视图(这种情况一般是纯代码)
注意:在重写loadView方法的时候,不要调用父类的方法。
5.viewDidLoad
当loadView将view载入内存中,会进一步调用viewDidLoad方法来进行进一步设置。此时,视图层次已经放到内存中,通常,我们对于各种初始化数据的载入,初始设定、修改约束、移除视图等很多操作都可以这个方法中实现。
视图层次(view hierachy):因为每个视图都有自己的子视图,这个视图层次其实也可以理解为一颗树状的数据结构。而树的根节点,也就是根视图(root view),在UIViewController中以view属性。它可以看做是其他所有子视图的容器,也就是根节点。
- viewWillAppear
系统在载入所有的数据后,将会在屏幕上显示视图,这时会先调用这个方法,通常我们会在这个方法对即将显示的视图做进一步的设置。比如,设置设备不同方向时该如何显示;设置状态栏方向、设置视图显示样式等。
另一方面,当APP有多个视图时,上下级视图切换是也会调用这个方法,如果在调入视图时,需要对数据做更新,就只能在这个方法内实现。
- viewWillLayoutSubviews
view 即将布局其Subviews。 比如view的bounds改变了(例如:状态栏从不显示到显示,视图方向变化),要调整Subviews的位置,在调整之前要做的工作可以放在该方法中实现
8.viewDidLayoutSubviews
view已经布局其Subviews,这里可以放置调整完成之后需要做的工作。
9.viewDidAppear
在view被添加到视图层级中以及多视图,上下级视图切换时调用这个方法,在这里可以对正在显示的视图做进一步的设置。
10.viewWillDisappear
在视图切换时,当前视图在即将被移除、或被覆盖是,会调用该方法,此时还没有调用removeFromSuperview。
11.viewDidDisappear
view已经消失或被覆盖,此时已经调用removeFromSuperView;
12.dealloc
视图被销毁,此次需要对你在init和viewDidLoad中创建的对象进行释放。
13.didReceiveMemoryWarning
在内存足够的情况下,app的视图通常会一直保存在内存中,但是如果内存不够,一些没有正在显示的viewController就会收到内存不足的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只会释放内存,并不会释放对象的所有权,所以通常我们需要在这里将不需要显示在内存中保留的对象释放它的所有权,将其指针置nil。
12.线程与进程的区别和联系?
->进程是指程序执行时的一个实例。
->线程是进程的一个实体。
进程——资源分配的最小单位,线程——程序执行的最小单位。
线程进程的区别体现在几个方面:
1):因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。
2):体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。
3).属于同一个进程的所有线程共享该进程的所有资源,包括文件描述符。而不同过的进程相互独立。
4).线程又称为轻量级进程,进程有进程控制块,线程有线程控制块;
5).线程必定也只能属于一个进程,而进程可以拥有多个线程而且至少拥有一个线程;
线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段)。
进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程是线程的容器,真正完成代码执行的线程,而进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的所有线程共享当前进程所拥有的资源。
21.OC的优缺点
优点:
1.Category是非常实用的扩展机制,可以很方便的为一个已有的类添加属性或者方法,而不需要笨拙的去继承。
2.运行时多态的概念,可以让一个类的对象动态的以其他类行为去执行(OC中多了很多运行时态的机制,其中id的特殊用途,可以通过id类型的变量,调用不同类的同名函数,即使这 些类没有任何关系)。
3.ARC自动管理内存
4.由于都是C衍生出的面向对象的语言 所以可以和C++混合编码
缺点:
1.不支持多重继承 只有多级继承。 (可以通过分类和协议实现多重继承)
2.使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
3.不支持运算符重载