应用程序的生命周期
应用程序是您的自定义代码和系统框架之间的复杂相互作用。系统框架提供了所有应用程序需要运行的基础架构,然后你提供自定义代码,自定义代码定制于基础框架并为应用程序提供所需的外观和感觉。为了有效地做到些,需要了解关于iOS基础框架及其工作原理。
iOS框架在实现过程中,依赖于诸如MVC和委托等设计模式。对于成功创建应用程序,了解这些设计模式至关重要。它还有助于熟悉Objective-C语言及其函数。如果您是iOS编程的新手,请阅读 Start Developing iOS Apps (Swift) ,了解iOS应用程序和Objective-C语言的介绍。
主函数
每个C的应用程序的入口点是主函数,因此iOS应用程序也一样。不同的是,对于iOS应用程序,你不需要自己写主函数。相反,Xcode会将此函数创建为基本项目的一部分。清单2-1显示了此函数的示例。除特例外,你不应改变Xcode提供的主函数的实现。
清单2-1 iOS应用程序的main
函数
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
唯一需要提的是,主函数的工作是人工控制UIKit框架。 UIApplicationMain函数通过创建应用程序的核心对象,从可用的storyboard
文件加载应用程序的用户界面,调用您的自定义代码,以便您有机会进行一些初始设置,并运行应用程序的运行循环,来控制进程。你必须提供的部分是storyboard
文件和自定义的初始化代码。
应用程序的结构
在启动期间,UIApplicationMain函数设置几个关键对象,并启动应用程序运行。每个iOS应用程序的核心是UIApplication对象,其工作是方便系统和应用程序中的其他对象之间的交互。图2-1显示了大多数应用程序中常见的对象,表2-1列出了每个对象所扮演的角色。首先要注意的是iOS应用程序使用MVC架构。这种模式将应用程序的数据和业务逻辑与数据的可视化呈现分开。此架构对于创建在不同屏幕尺寸的不同设备上运行的应用程序至关重要。
图2-1 iOS应用程序的关键对象
目的 | 描述 |
---|---|
UIApplication 对象 |
UIApplication 对象管理事件循环和其他高层次应用的行为。它还报告关键应用的过渡和一些特殊事件(如传入的推送通知)到其委托,这是你定义一个自定义对象。按原样使用UIApplication 象,即不进行子类化。 |
App的委托对象 | 应用代理是您的自定义代码的核心。此对象与UIApplication 对象协同工作,以处理应用程序初始化,状态转换和许多高级应用程序事件。这个对象也是唯一一个一定存在于每个应用程序,因此它通常用于设置应用程序的初始数据结构。 |
文档和数据模型对象 | 数据模型对象存储您应用的内容,并且特定于您的应用。例如,银行应用可以存储包含金融交易的数据库,而绘画应用可以存储图像对象或甚至导致创建该图像的绘图命令序列。 (对与后一种情况,图像对象仍然是数据对象,因为它只是图像数据的容器)。应用程序还可以使用文档对象(UIDocument )来管理数据模型对象。文档对象不是必需的,但提供了一种方便的方法来对单个文件或文件包的数据进行分组。有关文档的详细信息,请参阅 Document-Based App Programming Guide for iOS |
视图控制器对象 | 控制器对象管理应用程序内容在屏幕上的显示。视图控制器管理单个视图及其子视图集合。呈现时,视图控制器通过将它们安装在应用程序的窗口中来使其视图可见。UIViewController 是所有视图控制器对象的基类。它提供了默认功能,用于加载视图,呈现视图,响应设备旋转,以及其他一些标准系统行为。 UIKit和其他框架定义了额外的视图控制器类来实现标准系统接口,如图像选择器,选项卡栏接口和导航界面。有关如何使用视图控制器的详细信息,请参阅 View Controller Programming Guide for iOS |
UIWindow 对象 |
UIWindow 对象协调屏幕上的一个或多个视图的呈现。大多数应用程序只有一个窗口,在主屏幕上显示内容,但应用程序可能有一个额外的窗口显示在外部显示器上的内容。要更改应用程序的内容,请使用视图控制器更改在相应窗口中显示的视图。禁止更换窗口本身。除了托管视图,Windows还使用UIApplication 对象将事件传递到视图和视图控制器。 |
视图对象, 控件对象和 图层对象 | 视图和控件提供了应用内容的可视化展示。 视图是在指定的矩形区域中绘制内容并响应该区域内的事件的对象。 控件是一种特殊类型的视图,负责实现熟悉的界面对象,如按钮,文本字段和切换开关。UIKit框架提供了呈现许多不同类型的内容标准视图。通过子类化UIView ,还可以自定义视图。除了包含视图和控件,应用程序还可以将Core Animation图层合并到其视图和控件层次结构中。 图层对象实际上是表示可视内容的数据对象。 视图在幕后使用层对象来渲染其内容。 您还可以向接口添加自定义图层对象,以实现复杂的动画和其他类型的复杂视觉效果。 |
应用之间的区别在于它管理的数据(以及相应的业务逻辑)以及如何将该数据呈现给用户。 大多数与UIKit对象的交互并不定义您的应用程序,但可以帮助您改进其行为。 例如,您的应用程序委托的方法让您知道应用程序何时更改状态,以便您的自定义代码可以正确响应。
主运行循环
应用程序的主运行循环处理所有与用户相关的事件。 UIApplication
对象在启动时设置主运行循环,并使用它来处理事件并处理视图界面的更新。顾名思义,主要运行循环在应用程序的主线程上执行。此行为确保,按接收的顺序串行处理与用户相关的事件。
图2-2显示了主运行循环的架构,以及用户事件如何导致应用程序执行的操作。当用户与设备交互时,与这些交互相关的事件由系统生成,并通过UIKit设置的特殊端口传递到应用程序。事件由应用程序在内部排队,并逐个调度到主运行循环以供执行。 UIApplication
对象是接收事件的第一个对象,并且决定需要做什么。触摸事件通常被分派到主窗口对象,该主体窗口对象又将其分派到发生触摸的视图。其他事件可能因不同的应用程序对象而采用略有不同的路径。
图2-2 在主运行循环处理事件
许多类型的事件可以在iOS应用程序中传递。 最常见的如表2-2所示。 许多事件类型是由应用程序的主运行循环提供的,但有些不是。 事件被传递给一个委托对象或block。 有关如何处理大多数事件(包括触摸,遥控,运动,加速度计和陀螺仪事件)的信息 — 看 Event Handling Guide for iOS。
事件类型 | 传递给 | 笔记 |
---|---|---|
触摸 | 发生事件的视图对象 | 视图是响应者对象。视图未处理的任何触摸事件都会转发到响应链以进行处理。 |
遥控 摇移动事件 | 第一响应者对象 | 遥控事件是用于控制媒体播放功能,由耳机等配件产生。 |
加速度计 磁力仪 陀螺仪 | 您指定的对象 | 有关加速度计,磁力计,陀螺仪硬件的事件,会被传递到您指定的对象。 |
位置 | 您指定的对象 | 您使用核心位置框架,注册接收位置的事件。有关使用核心位置的详细信息,请参阅Location and Maps Programming Guide |
重绘 | 需要更新的视图 | 重绘事件不涉及事件对象,但仅仅是调用视图绘制本身。适用于iOS的绘图架构中描述的 Drawing and Printing Guide for iOS |
某些事件(如触摸和远程控制事件)由应用程序的响应程序对象处理。 响应者对象在您的应用程序无处不在。 (UIApplication
对象,视图对象和视图控制器对象都是响应者对象的例子。)大多数事件目标特定的响应者对象,但如果需要处理事件,可以传递给其他响应者对象(通过响应者链)。 例如,不处理事件的视图可以将事件传递到其超级视图或视图控制器。
餐厅 | 人均 | 地点 | 菜品 | 评价 |
---|---|---|---|---|
左稻 | 140 | JFC建发·品尚中心(2.2km) | 闽菜 | 创意美食,强烈推荐!! |
卷福餐厅 | 100 | 洪文大排档(2.1km) | 炒菜 | 酸菜鱼,小龙虾,老北京疙瘩汤 |
潮福城 | 110 | 湾悦城(3.4km) | 港式茶点 | 厦门最人气的老字号茶餐厅 |
君泰 | 80 | 前埔(2.3km) | 港式茶点 | 口味最佳自助餐,可自助可单点 |
戏锅 | 120 | 湾悦城(3.4km) | 火锅 | 小火锅界的翘楚 |
小龙坎 | 90 | 湾悦城(3.4km) | 火锅 | 四川火锅,热热闹闹 |
大丰收 | 110 | 万达或瑞景 | 鱼锅,火锅 | 福建连锁店 |
鱼旨寿司 | 120 | 万达 | 日本料理 | 精致料足 |
汉拿山烤肉 | 100 | 万达 | 韩国料理 | 爱吃肉的得去 |
在控件(例如按钮)中发生的触摸事件与在许多其他类型的视图中发生的触摸事件不同。 通常只有有限数量的与控件的可能的交互,并且因此这些交互被重新打包到动作消息中并且被传递到适当的目标对象。 此目标操作设计模式使您可以轻松地使用控件来触发应用中的自定义代码的执行。
应用执行状态
在任何给定时刻,您的应用都会处于表2-3中列出的某个状态。 系统会根据整个系统中发生的操作切换应用的状态。 例如,当用户按下主页按钮时,或电话呼叫进入,或发生另外的中断时间,当前运行的应用会改变状态。 图2-3显示了应用程序在切换状态时所采用的路径。
状态 | 描述 |
---|---|
不运行 | 应用程序还没有启动,或正在运行但被系统终止。 |
不活跃 | 应用程序在前台运行,但目前没有接收事件。(可能是在执行其他代码)。一个应用程序通常停留在这种状态下,只因为它要过渡到不同的状态。 |
活跃 | 该应用程序在前台运行,并接收事件。这是应用程序的正常前台模式。 |
后台 | 应用程序在后台并执行代码。 大多数应用程序进入此状态,只是为了后续的挂起状态。 但是,请求额外执行时间的应用程序可能会在此状态保持一段时间。 此外,直接在后台启动的应用程序将进入此状态,而不是处于非活动状态。 有关如何在后台执行代码的信息,请参阅Background Execution。 |
挂起 | 应用程序在后台,但没有执行代码。 系统会自动将应用程序移动到此状态,并在这样做之前不会通知他们。 挂起时,应用保留在内存中,但不会执行任代码。当内存不足时,系统可以清除已挂起的应用程序,而并不会通知应用,为前台应用程序腾出更多空间。 |
图2-3 iOS应用程序的状态变化
大多数状态转换,会随着你的应用程序调用委托对象的方法。这些方法是你以适当的方式响应状态的变化的时机。这些方法在下面列出,与如何使用它们的汇总。
application:willFinishLaunchingWithOptions:
- 此方法是在你的应用启动时,执行代码的首次机会。application:didFinishLaunchingWithOptions:
- 此方法允许您在向用户显示应用程序之前,执行最后的初始化。applicationDidBecomeActive:
-让您的应用程序知道它即将成为前台应用程序。使用此方法进行最后一分钟的准备。applicationWillResignActive:
-让你知道你的应用程序从前台状态退出。使用这个方法将你的应用程序进入静默状态。applicationDidEnterBackground:
-让您知道您的应用程序现在在后台运行,并可以在任何时候挂起。applicationWillEnterForeground:
-让你知道你的程序是移出后台,回到前台,但它是尚未激活。applicationWillTerminate:
-让您知道您的应用程序被终止。如果您的应用程序挂起,此方法不会被调用。
应用终止
应用程序必须随时准备好终止,不应该为保存用户数据或执行其他关键任务而等待。系统发起的终止是应用程序的生命周期的一个正常部分。系统通常终止应用程序,以便它可以回收内存,让用户有空间启动其他App,但系统也可能会终止行为不端或不响应事件的及时应用。
已挂起的应用在终止时不会收到通知;该系统杀死该进程并回收相应的内存。如果应用程序当前正在后台运行且未挂起,则系统在终止前调用其应用程序代理的applicationWillTerminate:
。当设备重新启动时,系统不会调用此方法。
除了系统终止您的应用程序,用户可以使用多任务UI,显式终止您的应用程序。用户的终止与终止已挂起的应用具有相同的效果。应用程序的进程将被终止,并且不会向应用程序发送通知。
线程和并发
系统创建应用程序的主线程,您可以根据需要创建其他线程来执行其他任务。对于iOS应用程序,首选技术是使用Grand Central Dispatch(GCD),操作对象和其他异步编程接口,而不是自己创建和管理线程。 GCD等技术允许您定义要执行的工作和要执行的任务,但让系统决定如何在可用的CPU上执行该工作。让系统处理线程管理简化了必须编写的代码,使得更容易确保代码的正确性,并提供更好的总体性能。
当考虑线程和并发性,考虑以下几点:
- 涉及视图,Core Animation和许多其他UIKit类的工作,通常必须发生在应用程序的主线程上。 这个规则有一些例外 - 例如,基于图像的操作通常可以在后台线程上发生 - 但是当有疑问时,假设工作需要在主线程上进行。
- 冗长的任务(或潜在的长度任务)应始终在后台线程上执行。任何涉及网络接入,文件访问,或大量数据处理的任务都应该使用GCD或操作对象异步执行。
- 在启动时,尽可能将任务移出主线程。在启动时,您的应用程序应尽快设置其用户界面。只有有助于设置用户界面的任务应该在主线程上执行。所有其他任务应异步执行,结果显示给用户,一旦他们准备好。
有关使用GCD和操作对象执行任务的详细信息,请参阅Concurrency Programming Guide。