07iOS应用程序的生命周期

0. 引子

iOS应用程序一般都是由自己编写的代码系统框架(system frameworks)组成,系统框架提供一些基本infrastructure给所有app来运行,而你提供自己编写的代码来定制app的外观和行为。因此,了解iOS infrastructure和它们如何工作对编写app是很有帮助的。

1. Main函数入口

所有基于C编写的app的入口都是main
函数,但iOS应用程序有点不同。不同就是你不需要为iOS应用程序而自己编写main
函数,当你使用Xcode创建工程的时候就已经提供了。除非一些特殊情况,否则你不应该修改Xcode提供的main
函数实现。示例代码如下:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[])
{ 
     @autoreleasepool { 
           return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
     } 
}

上面实例代码中有一个很重要的函数UIApplicationMain,它主要是创建app的几个核心对象来处理以下过程:

  • 1.从可用Storyboard文件加载用户界面
  • 2.调用AppDelegate自定义代码来做一些初始化设置
  • 3.将app放入Main Run Loop环境中来响应和处理与用户交互产生的事件

2.应用程序的架构

2.1 MVC

iOS应用程序都遵循Model-View-Controller
的架构,Model负责存储数据和处理业务逻辑,View负责显示数据和与用户交互,Controller是两者的中介,协调Model和View
相互协作。它们的通讯规则如下:

  • 1.Controller能够访问Model和View,Model和View不能互相访问
MVC
  • 2.当View与用户交互产生事件时,使用target-action方式来处理
Stanford University
  • 3.当View需要处理一些特殊UI逻辑或获取数据源时,通过delegate或data source方式交给Controller来处理
View
  • 4.Model不能直接与Controller通信,当Model有数据更新时,可以通过Notification或KVO (Key Value Observing)来通知Controller更新View
Model

2.2 几个关键对象

了解iOS的MVC设计模式之后,我们从下图来了解在MVC模式下iOS应用程序有哪些关键对象以及它们职责主要是什么?

The Structure of an App
  • 1.UIApplication对象
    用户与iOS设备交互时产生的事件(Multitouch Events,Motion Event,Remote Control Event)交由UIApplication
    对象来分发给control objects(UIControl)对应的target objects来处理并且管理整个事件循环,而一些关于app运行时重要事件委托给app delegate
    来处理。

  • 2.App delegate对象
    App delegate对象遵循UIApplicationDelegate
    协议,响应app运行时重要事件(app启动、app内存不足、app终止、切换到另一个app、切回app),主要用于app在启动时初始化一些重要数据结构;例如,初始化UIWindow,设置一些属性,为window添加rootViewController。

  • 3.View controller对象
    View Controller有一个view属性是view层次结构中的根view,你可以添加子view来构建复杂的view;controller有一些viewDidLoad、viewWillAppear等方法来管理view的生命周期;由于它继承UIResponder,所有还会响应和处理用户事件。

  • 4.Documents和data model对象
    data model对象主要用来存储数据。例如,饿了么app在搜索切换地址后,有历史记录搜索地址历史,当app下次启动时,读取和显示搜索地址历史。document对象(继承UIDocument)用来管理一些或所有的data model对象。document对象并不是必须的,但提供一种方便的方式来分组属于单个文件或多个文件的数据。

  • 5.UIWindow对象UIWindow
    对象位于view层次结构中的最顶层,它充当一个基本容器而不显示内容,如果想显示内容,添加一个content view到window。它也是继承UIResponder
    ,所以它也是会响应和处理用户事件。

  • 6.Viewcontrollayer对象
    View对象可以通过addSubview和removeFromSuperview 等方法管理view的层次结构,使用layoutIfNeeded和setNeedsLayout等方法布局view的层次结构,当你发现系统提供view已经满足不了你想要的外观需求时,可以重写drawRect方法或通过layer属性来构造复杂的图形外观和动画。还有一点,UIView也是继承UIResponder,所以也能够处理用户事件
    Control对象通常就是处理特定类型用户交互的View,常用的有button、switch、text field等。
    除了使用View和Control来构建view层次结构来影响app外观之外,还可以使用Core Animation框架的Layer对象来渲染view外观和构建复杂的动画。

3. Main Run Loop

一个iOS应用程序的main run loop主要作用是处理所有与用户相关的事件。UIApplication对象在启动时就设置main run loop和使用它来处理事件和更新基于view的界面。正如它名字所示,main run loop是运行在应用程序的主线程。这样就确保与接收到用户相关的事件被有序地处理。

下图显示main run loop的架构和用户事件最终是怎样被应用程序处理。当用户与设备交互时,系统就会生成与交互关联的事件,然后被应用程序的UIKit通过一个特殊的端口来分发。应用程序把事件放入队列,然后逐个分发到main run loop来执行。UIApplication
对象是第一个对象接收到事件,然后决定怎样处理它。一个touch event通常都被分发到main window对象,然后依次分发到发生触碰的view。其他event的接收事件对象路径可能有点不同。

进程

大多数的事件通过使用main run loop来分发,但有些不是。有些事件被发送到一个delegate对象或传递到你提供的block中。想了解更多如何处理大多数类型的事件,其中包括touch、remote control、motion、accelerometer和gyroscopic等事件,请查阅Event Handle Guide for iOS

4. 应用程序的状态和多任务

4.1

有时系统会从app一种状态切换另一种状态来响应系统发生的事件。例如,当用户按下home键、电话打入、或其他中断发生时,当前运行的应用程序会切换状态来响应。应用程序的状态有以下几种:

App状态切换
  • Not running:app还没运行
  • Inactive:app运行在foreground但没有接收事件
  • Active:app运行在foreground和正在接收事件
  • Background:运行在background和正在执行代码
  • Suspended:运行在background但没有执行代码

4.2

大多数发生状态转换时都会调用delegate对象对应的方法来响应app的状态改变。下面汇总了delegate对象的所有方法,当app状态发生转换时,你可能会使用到它们。

  • application:willFinishLaunchingWithOptions: - 这个方法是你在启动时的第一次机会来执行代码
  • application:didFinishLaunchingWithOptions: - 这个方法允许你在显示app给用户之前执行最后的初始化操作
  • applicationDidBecomeActive: - app已经切换到active状态后需要执行的操作
  • applicationWillResignActive: - app将要从前台切换到后台时需要执行的操作
  • applicationDidEnterBackground: - app已经进入后台后需要执行的操作
  • applicationWillEnterForeground: - app将要从后台切换到前台需要执行的操作,但app还不是active状态
  • applicationWillTerminate: - app将要结束时需要执行的操作

4.3

现在讲下app启动、来回切换app和锁屏时状态的切换和调用对应哪些delegate对象的方法:

  • app启动和active/inactive
    图1

如图所示,当app启动时,首先由not running状态切换到inactive状态,此时调用application:didFinishLaunchingWithOptions:方法;然后由inactive状态切换到active状态,此时调用applicationDidBecomeActive:方法。

图2

当app发生中断时,由active状态切换到inactive状态,此时调用applicationWillResignActive:方法。

  • 来回切换app
    图3

如图所示,当切换到另一个app时,由状态active切换到inactive,此时调用applicationWillResignActive:方法;然后从inactive状态切换到running状态,此时调用applicationDidEnterBackground:方法。

图4

而当切换回本来的app时,由running状态切换到inactive状态,此时调用applicationWillEnterForeground:方法,然后由inactive状态切换到active状态,调用applicationDidBecomeActive:方法。

  • 锁屏
    图5

如何所示,当手机锁屏时,由状态active
切换到inactive
,此时调用applicationWillResignActive:
;然后再由inactive
状态切换到running
状态,此时调用applicationDidEnterBackground:
方法。
更多关于app状态切换以及调用app delegate
哪些方法,请观看WWDC 2011 Session的session_320__adopting_multitasking_in_your_app视频。

5. 应用程序的终止

系统常常是为其他app启动时由于内存不足而回收内存最后需要终止应用程序,但有时也会是由于app很长时间才响应而终止。如果app当时运行在后台并且没有暂停,系统会在应用程序终止之前调用applicationWillTerminate:来保存用户的一些重要数据以便下次启动时恢复到app原来的状态。

6. 总结

本文总结了iOS应用程序从启动到结束过程中有哪些关键对象在参与,以及当用户与系统交互时产生事件时,系统利用main run loop来管理事件循环,决定将事件交给系统哪些对象处理和如何处理。而当app启动、来回切换app和锁屏时,app的状态如何切换和调用对应的哪些app delegate对象来处理。

7. 扩展阅读

App Programming Guide for iOS
Developing iOS 7 App for iPhone and iPad
深入理解RunLoop
Objective-C Autorelease Pool 的实现原理

20180322iOS应用生命周期总结

应用程序的状态:

  • Not running未运行:程序没启动。

  • Inactive未激活:程序在前台运行,不过没有接收到事件。在没有事件处理情况下程序通常停留在这个状态。

  • Active激活:程序在前台运行而且接收到了事件。这也是前台的一个正常的模式。

  • Backgroud后台:程序在后台而且能执行代码,大多数程序进入这个状态后会在在这个状态上停留一会。时间到之后会进入挂起状态(Suspended)。有的程序经过特殊的请求后可以长期处于Backgroud状态。

  • Suspended挂起:程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。

iOS的入口在main.m文件

int main(int argc, char *argv[])
{
      @autoreleasepool {
                 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
      }
 }

main函数的两个参数,iOS中没有用到,包括这两个参数是为了与标准ANSI C保持一致。 UIApplicationMain函数,前两个和main函数一样,重点是后两个。

后两个参数分别表示程序的主要类(principal class)和代理类(delegate class)。如果主要类(principal class)为nil,将从Info.plist中获取,如果Info.plist中不存在对应的key,则默认为UIApplication;如果代理类(delegate class)将在新建工程时创建。

根据UIApplicationMain函数,程序将进入AppDelegate.m,这个文件是xcode新建工程时自动生成的。下面看一下AppDelegate.m文件,这个关乎着应用程序的生命周期。

1、application didFinishLaunchingWithOptions:当应用程序启动时执行,应用程序启动入口,只在应用程序启动时执行一次。若用户直接启动,lauchOptions内无数据,若通过其他方式启动应用,lauchOptions包含对应方式的内容。

2、applicationWillResignActive:在应用程序将要由活动状态切换到非活动状态时候,要执行的委托调用,如 按下 home 按钮,返回主屏幕,或全屏之间切换应用程序等。

3、applicationDidEnterBackground:在应用程序已进入后台程序时,要执行的委托调用。

4、applicationWillEnterForeground:在应用程序将要进入前台时(被激活),要执行的委托调用,刚好与applicationWillResignActive 方法相对应。

5、applicationDidBecomeActive:在应用程序已被激活后,要执行的委托调用,刚好与applicationDidEnterBackground 方法相对应。

6、applicationWillTerminate:在应用程序要完全推出的时候,要执行的委托调用,这个需要要设置UIApplicationExitsOnSuspend的键值。

初次启动:

iOS_didFinishLaunchingWithOptions
iOS_applicationDidBecomeActive

按下home键:

iOS_applicationWillResignActive
iOS_applicationDidEnterBackground

点击程序图标进入:

iOS_applicationWillEnterForeground
iOS_applicationDidBecomeActive

当应用程序进入后台时,应该保存用户数据或状态信息,所有没写到磁盘的文件或信息,在进入后台时,最后都写到磁盘去,因为程序可能在后台被杀死。释放尽可能释放的内存。

 - (void)applicationDidEnterBackground:(UIApplication *)application{
 }

方法有大概5秒的时间让你完成这些任务。如果超过时间还有未完成的任务,你的程序就会被终止而且从内存中清除。

如果还需要长时间的运行任务,可以在该方法中调用:

[application beginBackgroundTaskWithExpirationHandler:^{ 
      NSLog(@"begin Background Task With Expiration Handler"); 
 }];

程序终止

程序只要符合以下情况之一,只要进入后台或挂起状态就会终止:

①iOS 4.0以前的系统

②app是基于iOS 4.0之前系统开发的。

③设备不支持多任务

④在Info.plist文件中,程序包含了 UIApplicationExitsOnSuspend 键。

系统常常是为其他app启动时由于内存不足而回收内存最后需要终止应用程序,但有时也会是由于app很长时间才响应而终止。如果app当时运行在后台并且没有暂停,系统会在应用程序终止之前调用app的代理的方法 - (void)applicationWillTerminate:(UIApplication *)application,这样可以让你可以做一些清理工作。你可以保存一些数据或app的状态。这个方法也有5秒钟的限制。超时后方法会返回程序从内存中清除。

注意:用户可以手工关闭应用程序

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容