应用是自定义代码和系统框架之间复杂的交互结果。系统框架提供所有应用运行所需的基础架构,你提供自定义框架及你想要的应用观感所需的代码。想要做到这样的效果,理解一点关于iOS基础框架及其工作原理是很有帮助的。
iOS框架依赖于设计模式,例如MVC和委托。理解这些设计模式对于成功创建应用至关重要。它也有助于熟悉OC语言及其功能。如果你是iOS编程新手,请先阅读Start Developing iOS Apps (Swift),来了解iOS应用和OC语言。
Main函数
每个C基础的应用的入口都是main函数,iOS应用也不例外。不同之处在于,iOS应用不需要你亲自写main函数。相反,Xcode创建了这个函数作为你的基础项目的一部分。代码清单2-1显示了这个函数的一个例子。除了少数例外,你不需要改变Xcode提供的这个main函数的实现。
代码清单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]));
}
}
Main函数唯一提及的事是,它的工作是把控制权交给UIKit框架。UIApplicationMain函数通过创建应用的核心对象来处理这个过程,从可用的storyboard文件加载应用的用户界面、调用你的自定义代码使你有机会进行一些初始化设置、并且让应用的运行时循环动起来。你需要提供的仅仅是storyboard文件和自定义初始化代码。
一个应用的结构
在启动期间,UIApplicationMain函数设置了介个关键对象,并启动了应用运行。每个iOS应用的核心是UIApplication对象,它的工作就是促使系统和应用中的其他对象进行交互。图2-1展示了大多数应用中常见的对象,同时,表2-1罗列了每个对象所扮演的角色。首先要注意iOS应用使用了MVC结构。这种设计模式将数据和业务逻辑从数据的可视化呈现中分离出来。这种结构对于创建适配各种屏幕的设备的应用至关重要。
图2-1 iOS应用中的关键对象
在iOS应用中,很多类型的事件可以被传送。最常见的已在表2-2中列出。很多这些事件类型使用应用的主运行循环,但是也有一些不是。一些事件被发送到一个委托对象或者被传递给一个你提供的block。关于如何处理大多数事件的类型的信息——包括触摸、遥控器、运动、加速计、陀螺仪等事件——参见Event Handling Guide for UIKit Apps。
表2-2 iOS应用常见事件类型
事件类型 | 传递到... | 注意 |
---|---|---|
触摸 | 事件发生的视图对象 | 视图时响应者对象。任何在这个视图中没有处理的事件都被发送到响应者链中进行处理。 |
遥控器 摇动运动事件 |
第一响应者对象 | 遥控器事件用于控制媒体播放,由耳机和其他附件产生。 |
加速计 磁力计 陀螺仪 |
你指定的对象 | 与加速计、磁力计、陀螺仪等硬件相关的事件被传颂到你指定的对象。 |
位置 | 你指定的对象 | 你使用 Core Location框架来注册接收位置事件。更多关于使用Core Location的信息,参见Location and Maps Programming Guide。 |
重绘 | 需要更新的视图 | 重绘事件不涉及事件对象,但是为了重绘视图自身,该事件会简单的调用视图。iOS绘图层次结构在 Drawing and Printing Guide for iOS中描述。 |
一些事件,例如触摸和遥控器事件,都是通过应用的响应者对象处理的。响应者对象在应用中随处可见。(UIApplication对象、视图对象、视图控制器对象都是响应者对象的例子。如果需要处理一个事件,绝大多数事件的目标是一个特定的响应者对象,但是它也能被传递到其他响应者对象(沿着响应者链)。例如,视图没有处理一个事件,这个视图会把这个事件传递给它的父视图或者它的视图控制器。
在控件(例如按钮)中发生的触摸事件与发生在很多其他类型的视图上的触摸事件比起来,处理上会有所不同。控件的可能的交互数量一般都有限定,这些交互被打包进动作消息然后发送给合适的目标对象。这种目标动作(target-action)设计模式可以让控件触发应用中的自定义代码执行变得很容易。
应用的执行状态
在任何给定的瞬间,应用必定处于表2-3罗列的状态中的一种。系统把应用从一个状态移动到另一个状态,以响应整个系统中发生的动作。例如,当用户点击Home键的时候、有电话呼入、或者发生其他任何中断事件,当前运行的应用会响应这些事件而改变状态。图2-3展示了当应用从一个状态转到另一个状态时所经历的路径。
表2-3 应用状态
状态 | 描述 |
---|---|
不运行 | 应用没有被启动或者曾经运行但是被系统终止。 |
非活跃 | 应用正在前台运行,但是当前没有接收事件(或许正在执行其他代码)。应用通常只在转换到其他状态时的短暂时间处于这种状态。 |
活跃 | 应用在前台执行,并且接受事件。这时前台的应用的通常状态。 |
后台 | 应用在后台执行代码。大多数应用在它们被挂起的时候会短暂停留在此状态。但是,如果应用请求了额外的执行时间,那么就可以保持这个状态一段时间。另外,在后台直接启动的应用会进入这个状态而不是非活跃(inactive)状态。关于如何在后台时执行代码,参见Background Execution。 |
挂起 | 应用在后台,但是它不执行代码。系统会无声无息地自动把应用移入这个状态。当挂起时,应用在内存中保留,但是不执行任何代码。 当出现低内存条件时,系统会悄无声息的清除挂起的应用,从而为前台应用提供更多空间。 |
图2-3 在iOS应用中的状态改变
大多数状态转换伴随着应用委托对象方法的相应调用。这些方法是一个以合适的方式响应状态改变的机会。这些方法及方法的使用方式摘要罗列如下。
- application:willFinishLaunchingWithOptions:——这个方法是应用启动时第一个执行代码的机会。
- application:didFinishLaunchingWithOptions:——这个方法允许你在应用显示给用户之前执行任何最终的初始化。
- applicationDidBecomeActive:——让应用知道它要变成前台应用。用这个方法做最后一刻的准备。
- applicationWillResignActive:——让你知道应用正在从前台应用往外转换。使用这个方法来把应用置于休眠状态。
- applicationDidEnterBackground:——让你知道应用现在正在后台运行并且可能在任何时刻被挂起。
- applicationWillEnterForeground:——让你知道应用正在移出后台回到前台,但是现在还没有到达活跃状态。
- applicationWillTerminate:——让你知道应用正在被终止。如果应用已被挂起那这个方法不会被调用。
应用终止
应用必须为随时发生的终止做好准备,并应该立刻保存数据或者执行其他关键的任务。系统发起的终止是应用生命周期的正常部分。系统通常终止应用,以便回收内存,并给用户启动的其他应用提供空间,但是系统也可能在应用发生错误或者没有及时响应事件的时候终止它。
挂起应用在它们被终止的时候不会收到任何通知;系统杀死进程并收回相关的内存。如果应用现在正在后台运行没有被挂起,系统会在种植之前调用它的委托方法applicationWillTerminate: 。在设备重启的时候系统不会调用这个方法。
除了系统终止你的应用之外,用户也可以使用多任务UI来主动的终止你的应用。用户发起的终止与终止挂起应用的效果是一样的。应用的进程被杀死且不会有通知发送给应用。
线程和并发
系统创建应用的主线程,如有必要你可以创建额外的线程来执行其他任务。对于iOS应用,首选技术是使用Grand Central Dispatch(GCD)、操作对象(operation object)、以及其他异步编程接口,而不是自己创建并管理线程。像GCD这样的技术让你定义你想做的事情以及做这些事情的顺序,但让系统决定如何在可用的CPU上执行该工作。让系统处理线程的管理可以简化你必须写的代码,使得确保代码的正确性变得容易,并且提供了更好的整体性能。
当思考关于线程和并发的时候,请考虑以下几点:
- 涉及视图、核心动画、以及其他UIKit类的工作,通常都发生在应用的主线程。虽也有一些例外——例如,基于图片的操作通常发生在后台线程——但当有疑问时,要假定这项工作需要在主线程中发生。
- 长时任务(或潜在的长时任务)应该总是在后台线程中执行。任何涉及网络访问、文件访问、或者大量数据处理的任务都应该使用GCD或者操作对象进行异步执行。
- 在启动时,尽可能的把任务从主线程移出。在启动时,应用应该尽可能快的设置用户界面。只有有助于设置用户界面的任务才应该在主线程上运行。所有任务都应该异步执行,一旦它们有结果就呈现个用户。
更多关于使用GCD和操作对象执行任务的信息,参见Concurrency Programming Guide。
网友评论