iOS 应用程序的生命周期
jopen
9年前
<p>iOS应用程序一般都是由 <strong>自己编写的代码</strong> 和 <strong>系统框架</strong> (system frameworks)组成,系统框架提供一些基本infrastructure给所有app来运行,而你提供自己编写的代码来定制app的外观和行为。因此,了解iOS infrastructure和它们如何工作对编写app是很有帮助的。 </p> <h2>Main函数入口 </h2> <p>所有基于C编写的app的 <strong>入口</strong> 都是main函数,但iOS应用程序有点不同。不同就是你不需要为iOS应用程序而自己编写main函数,当你使用Xcode创建工程的时候就已经提供了。除非一些特殊情况,否则你 <strong>不应该</strong> 修改Xcode提供的main函数实现。示例代码如下: </p> <pre class="brush:cpp; toolbar: true; auto-links: false;">#import <UIKit/UIKit.h> #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }</pre> <p>上面实例代码中有一个很重要的函数 <a href="/misc/goto?guid=4959623306532686921" target="_blank">UIApplicationMain</a> ,它主要是创建app的几个核心对象来处理以下过程: </p> <ol> <li>从可用Storyboard文件加载用户界面 </li> <li>调用AppDelegate自定义代码来做一些初始化设置 </li> <li>将app放入Main Run Loop环境中来响应和处理与用户交互产生的事件 </li> </ol> <h2>应用程序的架构 </h2> <p>iOS应用程序都遵循Model-View-Controller的架构,Model负责存储数据和处理业务逻辑,View负责显示数据和与用户交互,Controller是两者的中介,协调Model和View相互协作。它们的 <strong>通讯规则</strong> 如下: </p> <ol> <li> <p>Controller能够访问Model和View,Model和View不能互相访问 </p> <div href="https://simg.open-open.com/show/7f7c10b19cff18f6c304f16a2db47b10.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/7f7c10b19cff18f6c304f16a2db47b10.png" width="700" height="366" /> <p>MVC Communication - Reference from Stanford University.png </p> </div> </li> <li> <p>当View与用户交互产生事件时,使用target-action方式来处理 </p> <div href="https://simg.open-open.com/show/87996e2e91e7a95e631abc9a0df27e88.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/87996e2e91e7a95e631abc9a0df27e88.png" width="700" height="362" /> <p>MVC Communication - Reference from Stanford University.png </p> </div> </li> <li> <p>当View需要处理一些特殊UI逻辑或获取数据源时,通过delegate或data source方式交给Controller来处理 </p> <div href="https://simg.open-open.com/show/2d5f1a5efdb36c4d92d889544a5b7f8a.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/2d5f1a5efdb36c4d92d889544a5b7f8a.png" width="700" height="367" /> <p>MVC Communication - Reference from Stanford University.png </p> </div> </li> <li> <p>Model不能直接与Controller通信,当Model有数据更新时,可以通过Notification或KVO (Key Value Observing)来通知Controller更新View </p> <div href="https://simg.open-open.com/show/d07d94a28ea02320b53b6fe842bbdb91.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/d07d94a28ea02320b53b6fe842bbdb91.png" width="700" height="364" /> <p>MVC Communication - Reference from Stanford University.png </p> </div> </li> </ol> <p>了解iOS的MVC设计模式之后,我们从下图来了解在MVC模式下iOS应用程序有哪些 <strong>关键对象</strong> 以及它们 <strong>职责</strong> 主要是什么? </p> <div href="https://simg.open-open.com/show/284f5ed89dd52653e65e5ef8b9f9f78e.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/284f5ed89dd52653e65e5ef8b9f9f78e.png" width="700" height="618" /> <p>The Structure of an App.png </p> </div> <ul> <li> <div> <p><a href="/misc/goto?guid=4959636773981945849" target="_blank">UIApplication</a> 对象 </p> <p>用户与iOS设备交互时产生的事件(Multitouch Events,Motion Event,Remote Control Event)交由UIApplication对象来分发给 <strong>control objects</strong> ( <a href="/misc/goto?guid=4959636774101941497" target="_blank">UIControl</a> )对应的 <strong>target objects</strong> 来处理并且管理整个事件循环,而一些关于app运行时重要事件委托给app delegate来处理。 </p> </div> </li> <li> <div> <p><a href="/misc/goto?guid=4959636774220431322" target="_blank">App delegate</a> 对象 </p> <p>App delegate对象遵循UIApplicationDelegate协议,响应app运行时重要事件(app启动、app内存不足、app终止、切换到另一个app、切回app),主要用于app在启动时初始化一些重要数据结构;例如,初始化UIWindow,设置一些属性,为window添加rootViewController。 </p> </div> </li> <li> <div> <p><a href="/misc/goto?guid=4959636774329676342" target="_blank">View controller</a> 对象 </p> <p>View Controller有一个view属性是view层次结构中的 <strong>根view</strong> ,你可以添加子view来构建复杂的view;controller有一些viewDidLoad、viewWillAppear等方法来管理view的生命周期;由于它继承UIResponder,所有还会响应和处理用户事件。 </p> </div> </li> <li> <div> <p><a href="/misc/goto?guid=4959636774435467096" target="_blank">Documents</a> 和data model对象 </p> <p>data model对象主要用来存储数据。例如,饿了么app在搜索切换地址后,有历史记录搜索地址历史,当app下次启动时,读取和显示搜索地址历史。 </p> <p>document对象(继承UIDocument)用来管理一些或所有的data model对象。document对象并不是必须的,但提供一种方便的方式来分组属于单个文件或多个文件的数据。 </p> </div> </li> <li> <div> <p><a href="/misc/goto?guid=4959636774542495016" target="_blank">UIWindow</a> 对象 </p> <p>UIWindow对象位于view层次结构中的最顶层,它充当一个基本容器而不显示内容,如果想显示内容,添加一个content view到window。 </p> <p>它也是继承UIResponder,所以它也是会响应和处理用户事件。 </p> </div> </li> <li> <div> <p><a href="/misc/goto?guid=4959636774654874984" target="_blank">View</a> 、 <a href="/misc/goto?guid=4959636774761452798" target="_blank">control</a> 、 <a href="/misc/goto?guid=4959636774883910273" target="_blank">layer</a> 对象 </p> <p>View对象可以通过addSubview和removeFromSuperview 等方法 <strong>管理</strong> view的层次结构,使用layoutSubviews、layoutIfNeeded和layoutIfNeeded等方法 <strong>布局</strong> view的层次结构,当你发现系统提供view已经满足不了你想要的外观需求时,可以重写drawRect方法或通过layer属性来构造复杂的 <strong>图形外观和动画</strong> 。还有一点,UIView也是继承UIResponder,所以也能够 <strong>处理用户事件</strong> 。 </p> <p>Control对象通常就是处理特定类型用户交互的View,常用的有button、switch、text field等。 </p> <p>除了使用View和Control来构建view层次结构来影响app外观之外,还可以使用Core Animation框架的Layer对象来渲染view外观和构建复杂的动画。 </p> </div> </li> </ul> <h2>Main Run Loop </h2> <p>一个iOS应用程序的 <strong>main run loop</strong> 主要作用是处理所有与用户相关的事件。UIApplication对象在启动时就设置main run loop和使用它来处理事件和更新基于view的界面。正如它的名字显示,main run loop是运行在应用程序的主线程。这样就确保与接收到用户相关的事件被 <strong>有序地</strong> 处理。 </p> <p>下图显示main run loop的架构和用户事件最终是怎样被应用程序处理。当用户与设备交互时,系统就会生成与交互关联的事件,然后被应用程序的UIKit通过一个特殊的端口来分发。应用程序把事件放入队列,然后 <strong>逐个</strong> 分发到main run loop来执行。UIApplication对象是第一个对象接收到事件,然后决定怎样处理它。一个 <strong>touch event</strong> 通常都被分发到main window对象,然后依次分发到 <strong>发生触碰的view</strong> 。其他event的接收事件对象路径可能有点不同。 </p> <div href="https://simg.open-open.com/show/c924b067a229bbc25f92ee746c711422.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/c924b067a229bbc25f92ee746c711422.png" width="626" height="450" /> <p>Main Run Loop from Apple Document </p> </div> <p>大多数的事件通过使用main run loop来分发,但有些不是。有些事件被发送到一个delegate对象或传递到你提供的block中。想了解更多如何处理大多数类型的事件,其中包括touch、remote control、motion、accelerometer和gyroscopic等事件,请查阅 <a href="/misc/goto?guid=4959636774986958620" target="_blank">Event Handle Guide for iOS</a> 。 </p> <h2>应用程序的状态和多任务 </h2> <p>有时系统会从app一种状态切换另一种状态来响应系统发生的事件。例如,当用户按下home键、电话打入、或其他中断发生时,当前运行的应用程序会切换状态来响应。应用程序的状态有以下几种: </p> <div href="https://simg.open-open.com/show/c638db0c4c5e31eae388d6d33e5c6d47.png"> <img alt="iOS 应用程序的生命周期" src="https://simg.open-open.com/show/c638db0c4c5e31eae388d6d33e5c6d47.png" width="526" height="481" /> <p>App State from Apple Document </p> </div> <ul> <li>Not running:app还没运行 </li> <li>Inactive:app运行在foreground但没有接收事件 </li> <li>Active:app运行在foreground和正在接收事件 </li> <li>Background:运行在background和正在执行代码 </li> <li>Suspended:运行在background但没有执行代码 </li> </ul> <p>大多数发生状态转换时都会调用delegate对象对应的方法来响应app的状态改变。下面汇总了delegate对象的所有方法,当app状态发生转换时,你可能会使用到它们。 </p> <ul> <li>application:willFinishLaunchingWithOptions:- 这个方法是你在启动时的第一次机会来执行代码 </li> <li>application:didFinishLaunchingWithOptions:- 这个方法允许你在显示app给用户之前执行最后的初始化操作 </li> <li>applicationDidBecomeActive:- app已经切换到active状态后需要执行的操作 </li> <li>applicationWillResignActive:- app将要从前台切换到后台时需要执行的操作 </li> <li>applicationDidEnterBackground:- app已经进入后台后需要执行的操作 </li> <li>applicationWillEnterForeground:- app将要从后台切换到前台需要执行的操作,但app还不是active状态 </li> <li>applicationWillTerminate:- app将要结束时需要执行的操作 </li> </ul> <p>现在讲下app启动、来回切换app和锁屏时状态的切换和调用对应哪些delegate对象的方法: </p> <ul> <li> <p>app启动和active/inactive </p> <div href="https://simg.open-open.com/show/31e298e132e21477a09af4dcdbb08a05.gif"> <img src="https://simg.open-open.com/show/31e298e132e21477a09af4dcdbb08a05.gif" width="700" height="403.5977859778598" /> <p>Launch and active/inactive from Apple WWDC 2011 Session </p> </div> 如图所示,当app启动时,首先由not running状态切换到inactive状态,此时调用application:didFinishLaunchingWithOptions:方法;然后由inactive状态切换到active状态,此时调用applicationDidBecomeActive: <p>方法。 </p> <div href="https://simg.open-open.com/show/aec08aa243a2971254b97be1b043a8cc.gif"> <img src="https://simg.open-open.com/show/aec08aa243a2971254b97be1b043a8cc.gif" width="700" height="403.5977859778598" /> <p>Launch and active/inactive 2 from Apple WWDC 2011 Session </p> </div> <p>当app发生中断时,由active状态切换到inactive状态,此时调用applicationWillResignActive:方法。 </p> </li> <li> <p>来回切换app </p> <div href="https://simg.open-open.com/show/482928bd833de441d22840769cb418ab.gif"> <img src="https://simg.open-open.com/show/482928bd833de441d22840769cb418ab.gif" width="700" height="403.5977859778598" /> <p>Switch from an app from Apple WWDC 2011 Session </p> </div> 如图所示,当切换到另一个app时,由状态active切换到inactive,此时调用applicationWillResignActive:方法;然后从inactive状态切换到running状态,此时调用applicationDidEnterBackground: <p>方法。 </p> <div href="https://simg.open-open.com/show/084c3d5b3d8bf5886b9a191fba6593f4.gif"> <img src="https://simg.open-open.com/show/084c3d5b3d8bf5886b9a191fba6593f4.gif" width="700" height="403.5977859778598" /> <p>Switch to an app from Apple WWDC 2011 Session </p> </div> 而当切换回本来的app时,由running状态切换到inactive状态,此时调用applicationWillEnterForeground:方法,然后由inactive状态切换到active状态,调用applicationDidBecomeActive: <p>方法。 </p> </li> <li> <p>锁屏 </p> <div href="https://simg.open-open.com/show/c5f719685d146b4970677e60256a3dc8.gif"> <img src="https://simg.open-open.com/show/c5f719685d146b4970677e60256a3dc8.gif" width="700" height="403.5977859778598" /> <p>Device lock from Apple WWDC 2011 Session </p> </div> 如何所示,当手机锁屏时,由状态active切换到inactive,此时调用applicationWillResignActive:;然后再由inactive状态切换到running状态,此时调用applicationDidEnterBackground: <p>方法。 </p> <p>更多关于app状态切换以及调用app delegate哪些方法,请观看WWDC 2011 Session的 <a href="/misc/goto?guid=4959630420539456230" target="_blank">session_320__adopting_multitasking_in_your_app</a> 视频。 </p> </li> </ul> <h2>应用程序的终止 </h2> <p>系统常常是为其他app启动时由于内存不足而回收内存最后需要终止应用程序,但有时也会是由于app很长时间才响应而终止。如果app当时运行在后台并且没有暂停,系统会在应用程序终止之前调用applicationWillTerminate:来保存用户的一些重要数据以便下次启动时恢复到app原来的状态。 </p> <h2>总结 </h2> <p>本文总结了iOS应用程序从启动到结束过程中有哪些关键对象在参与,以及当用户与系统交互时产生事件时,系统利用main run loop来管理事件循环,决定将事件交给系统哪些对象处理和如何处理。而当app启动、来回切换app和锁屏时,app的状态如何切换和调用对应的哪些app delegate对象来处理。<br /> <br /> </p> <div> 原文 <a href="/misc/goto?guid=4959636775136979222">http://www.jianshu.com/p/aa50e5350852</a> </div> <p></p>