现在的位置: 首页 > 综合 > 正文

iPhone应用程序编程指南(核心应用程序)

2012年11月10日 ⁄ 综合 ⁄ 共 7845字 ⁄ 字号 评论关闭

 

 

核心应用程序架构

应用程序的生命周期

Application life cycle

 

主函数

main例程只做三件事:创建一个自动释放池,调用UIApplicationMain函数,以及使用自动释放池。

应用程序的委托

监控应用程序的高级行为是应用程序委托对象的责任,而应用程序委托对象是您提供的定制类实例。

应用程序的委托对象负责处理几个关键的系统消息。每个iPhone应用程序都必须有应用程序委托对象,它可以是您希望的任何类的实例,但需要遵循UIApplicationDelegate协议,该协议的方法定义了应用程序生命周期中的某些挂钩,您可以通过这些方法来实现定制的行为。

事件处理周期

在应用程序初始化之后,UIApplicationMain函数就会启动管理应用程序事件和描画周期的基础组件,如图1-2所示。在用户和设备进行交互的时候,iPhone OS会检测触摸事件,并将事件放入应用程序的事件队列。然后,UIApplication对象的事件处理设施会从队列的上部逐个取出事件,将它分发到最适合对其进行处理的对象。

The event and drawing cycle

在iPhone OS的多点触摸事件模型中,触摸数据被封装在事件对象(UIEvent)中。为了跟踪触摸动作,事件对象中包含一些触摸对象(UITouch),每个触摸对象都对应于一个正在触摸屏幕的手指。当用户把手指放在屏幕上,然后四处移动,并最终离开屏幕的时候,系统通过对应的触摸对象报告每个手指的变化。

 

在启动一个应用程序时,系统会为该程序创建一个进程和一个单一的线程。这个初始线程成为应用程序的主线程,UIApplication对象正是在这个线程中建立主运行循环及配置应用程序的事件处理代码。图1-3显示了事件处理代码和主运行循环的关系。系统发送的触摸事件会在队列中等待,直到被应用程序的主运行循环处理

Processing events in the main run loop

请注意:运行循环负责监视指定执行线程的输入源。当输入源有数据需要处理的时候,运行循环就唤醒相应的线程,并将控制权交给输入源的处理器代码。处理器在完成任务后将控制权交回运行循环,然后,运行循环就处理下一个事件。如果没有其它事件,运行循环会使线程进入休眠状态。您可以通过Foundation框架的NSRunLoop类来安装自己的输入源,包括端口和定时器。

UIApplication对象用一个处理触摸事件的输入源来配置主运行循环,使触摸事件可以被派发到恰当的响应者对象。响应者对象是继承自UIResponder类的对象,它实现了一或多个事件方法,以处理触摸事件不同阶段发生的事件。应用程序的响应者对象包括UIApplicationUIWindowUIView、及所有UIView子类的实例。应用程序通常将事件派发给代表应用程序主窗口的UIWindow对象,然后由窗口对象将事件传送给它的第一响应者,通常是发生触摸事件的视图对象(UIView)。

响应者链:它由应用程序中一组链接在一起的响应者对象组成,通常以第一响应者作为链的开始。当发生某个事件时,如果第一响应者对象不能处理,就将它传递给响应者链中的下一个对象。消息继续在链中传递—从底层的响应者对象到诸如窗口、应用程序、和应用程序委托这样的高级响应者对象—直到事件被处理。如果事件最终没有被处理,就会被丢弃。

基本设计模式

模型-视图-控制器 委托 目标-动作 委托内存模型

 

应用程序运行环境

启动过程快,使用时间短

除了快速启动,您的应用程序还必须做好快速退出的准备。每次用户离开您的应用程序时,无论是按下Home键还是通过软件提供的功能打开了另一个应用程序,iPhone OS会通知您的应用程序退出。在那个时候,您需要尽快将未保存的修改保存到磁盘上。如果您的应用程序退出的时间超过5秒,系统可能会立刻终止它的运行。

应用程序沙箱

在iPhone OS中,应用程序和它的数据驻留在一个安全的地方,其它应用程序都不能进行访问。

在应用程序安装之后,系统就通过计算得到一个不透明的标识,然后基于应用程序的根目录和这个标识构建一个指向应用程序家目录的路径。因此,应用程序的家目录具有如下结构:

  • /ApplicationRoot/ApplicationID/

在安装过程中,系统会创建应用程序的家目录和几个关键的子目录,配置应用程序沙箱,以及将应用程序的程序包拷贝到家目录上。将应用程序及其数据放在一个特定的地方可以简化备份-并-恢复操作,还可以简化应用程序的更新及卸载操作

虚拟内存系统

在iPhone OS中,每个程序都仍然有自己的虚拟地址空间,但其可用的虚拟内存受限于现有的物理内存的数量(这和Mac OS X不同)。这是因为当内存用满的时候,iPhone OS并不将非永久内存页面(volatile pages)写入到磁盘。相反,虚拟内存系统会根据需要释放永久内存(nonvolatile memory),确保为正在运行的应用程序提供所需的空间。内存的释放是通过删除当前没有正在使用或包含只读内容(比如代码页面)的内存页面来实现的,这样的页面可以在稍后需要使用的时候重新装载到内存中。

自动休眠定时器

将共享的UIApplication对象的idleTimerDisabled属性设置为YES,就可以禁用自动休眠定时器。

 

应用程序的程序包

程序包是文件系统中的一个目录,用于将执行代码和相关资源集合在一个地方。

信息属性列表Info.plist

在缺省情况下,Xcode不会直接显示实际的键名,因此,下表在括号中列出了这些键在Xcode中显示的字符串。您可以查看所有键的实际键名,具体做法是按住Control键的同时点击编辑器中的信息属性列表项目,然后选择上下文菜单中的Show Raw Keys/Values命令

如果信息属性文件中的属性值是显示在用户界面上的字符串,则应该进行本地化,特别是当Info.plist中的字符串值是与本地化语言子目录下InfoPlist.strings文件中的字符串相关联的键时。

应用程序图标和启动图像

将一个图像文件加入到Xcode工程的具体做法是从Project菜单中选择Add to Project命令,在浏览器中定位目标文件,然后点击Add按键。

 

处理关键的应用程序任务

初始化和终止

表1-5  应用程序委托的责任

委托方法

描述

applicationDidFinishLaunching:

使用这个方法来将应用程序恢复到上一个会话的状态。您也可以在这个方法中执行应用程序数据结构和用户界面的定制初始化。

applicationWillTerminate:

使用这个方法来将未存数据或关键的应用程序状态存入磁盘。您也可以在这个方法中执行额外的清理工作,比如删除临时文件。

响应中断

除了Home按键可以终止您的应用程序之外,系统也可以暂时中断您的应用程序,使用户得以响应一些重要的事件。举例来说,应用程序可能被呼入的电话、SMS信息、日历警告、或者设备上的Sleep按键所打断。按下Home按键会终止您的应用程序,而上述这些中断则只是暂时的。如果用户忽略这些中断,您的应用程序可以象之前那样继续运行;然而,如果用户决定接电话或回应SMS信息,系统就会开始终止您的程序。

图1-6  中断过程的事件流程

The flow of events during an interruption

根据用户对中断的不同响应,系统可能在中断结束之后再次启动您的应用程序。举例来说,如果用户接听一个电话并在完成后挂断,则系统会重新启动您的应用程序;如果用户在接听电话过程中回到Home屏幕或启动另一个程序,则系统就不再启动您的应用程序了。

重要提示:当用户接听电话并在通话过程中重新启动您的应用程序时,状态条的高度会变大,以反映当前用户正在通话中。类似地,当用户结束通话的时候,状态条的高度会缩回正常尺寸。您的应用程序应该为状态条高度的变化做好准备,并据此调整内容区域的尺寸。视图控制器会自动处理这个行为,然而,如果您通过代码进行用户界面的布局,就需要在视图布局以及通过layoutSubviews方法处理动态布局变化时考虑状态条的高度。

在运行您的应用程序时,如果用户按下设备的休眠/唤醒按键,系统会调用应用程序委托的applicationWillResignActive:方法,停止触摸事件的派发,然后使设备进入休眠状态。之后,当用户唤醒设备时,系统会调用应用程序委托的applicationDidBecomeActive:方法,并再次开始向应用程序派发事件。如同处理其它中断一样,您应该使用这些方法来使应用程序进入休眠状态(或者暂停游戏)及再次唤醒它们。在休眠时,您的应用程序应该尽可能少用电力。

 

观察低内存警告

UIKit提供如下几种接收低内存警告的方法:

定制应用程序的行为

以景观模式启动

UIInterfaceOrientation属性提示iPhone OS在启动时应该配置应用程序状态条(如果有的话)的方向,就象配置视图控制器管理下的视图方向一样。在iPhone OS 2.1及更高版本的系统中,视图控制器会尊重这个属性,将视图的初始方向设置为指定的方向。使用这个属性相当于在applicationDidFinishLaunching:方法的一开始执行UIApplicationsetStatusBarOrientation:animated:方法。

和其它应用程序进行通讯

如果一个应用程序支持一些已知类型的URL,您就可以通过对应的URL模式和该程序进行通讯。然而,在大多数情况下,URL只是用于简单地启动一个应用程序并显示一些和调用方有关的信息。

苹果内置支持httpmailtotel、和sms这些URL模式,还支持基于http的、指向Maps、YouTube、和iPod程序的URL。应用程序也可以自己注册定制的URL模式。

NSURL *myURL = [NSURL URLWithString:@"todolist://www.acme.com?Quarterly%20Report#200806231300"];
[[UIApplication sharedApplication] openURL:myURL];

重要提示:如果您的URL类型包含的模式和苹果定义的一样,则启动的是苹果提供的程序,而不是您的程序。如果有多个第三方的应用程序注册处理同样的URL模式,则该类型的URL由哪个程序处理是没有定义的。

实现定制的URL模式

您可以为自己的应用程序注册包含定制模式的URL类型。定制的URL模式是第三方应用程序和其它程序及系统进行交互的机制。通过定制的URL模式,应用程序可以将自己的服务提供给其它程序。

注册定制的URL模式

表1-6  CFBundleURLTypes属性的键和值

CFBundleURLName

这是个字符串,表示URL类型的抽象名。为了确保其唯一性,建议您使用反向DNS风格的标识,比如com.acme.myscheme

这里提供的URL类型名是一个指向本地化字符串的键,该字符串位于本地化语言包子目录中的InfoPlist.strings文件中。本地化字符串是人类可识别的URL类型名称,用相应的语言来表示。

CFBundleURLSchemes

这是个URL模式的数组,表示归属于这个URL类型的URL。每个模式都是一个字符串。属于指定URL类型的URL都带有它们的模式组件。

处理URL请求

应用程序委托在application:handleOpenURL:方法中处理传递给应用程序的URL请求。如果您已经为自己的应用程序注册了定制的URL模式,则务必在委托中实现这个方法。

基于定制模式的URL采用的协议是请求服务的应用程序能够理解的。URL中包含一些注册模式的应用程序期望得到的信息,这些信息是该程序在处理或响应URL请求时需要的。传递给application:handleOpenURL:方法的NSURL对象表示的是Cocoa Touch框架中的URL。NSURL遵循RFC 1808规范,该类中包含一些方法,用于返回RFC 1808定义的各个URL要素,包括用户名、密码、请求、片断、和参数字符串。与您注册的定制模式相对应的“协议”可以使用这些URL要素来传递各种信息。

请务必对传入的URL输入进行验证。如果您希望了解如何避免URL处理的相关问题,请参见安全编码指南文档中的验证输入部分。如果要了解苹果定义的URL模式,请参见苹果的URL模式参考

显示应用程序的偏好设置

如果您的应用程序通过偏好设置来控制其行为的不同方面,那么,以何种方式向用户提供偏好设置就取决于它们是否为程序的必需部分。

  • 如果偏好设置是程序使用的必需部分(且直接实现起来足够简单),那么应该直接通过应用程序的定制界面来呈现。

  • 如果偏好设置不是必需的,且要求相对复杂的界面,则应该通过系统的Settings程序来呈现。

在确定一组偏好设置是否为程序的必需部分时,请考虑您为程序设计的使用模式。如果您希望用户相对频繁地修改偏好设置,或者这些偏好设置对程序的行为具有相对重要的影响,则可能就是必需部分。

设置包是位于应用程序的程序包目录最顶层的定制资源,它是一个封装了的目录,名字为Settings.bundle。设置包中包含一些具有特别格式的数据文件(及其支持资源),其作用是告诉Settings程序如何显示您的偏好设置。这些文件还告诉Settings程序应该把结果值存储在偏好设置数据库的什么位置上,以便应用程序随后可以通过NSUserDefaultsCFPreferences API来进行访问。

国际化您的应用程序

用户选择的语言和程序包中的一个子目录相关联,该子目录名由两个部分组成,分别是ISO 639-1定义的语言码和.lproj后缀。您还可以对语言码进行修改,使之包含具体的地区,方法是在后面(在下划线之后)加入ISO 3166-1定义的区域指示符。举例来说,如果要指定美国英语的本地化资源,程序包中的子目录应该命名为en_US.lproj。我们约定,本地化语言子目录称为lproj文件夹。

性能和响应速度的调优

不要阻塞主线程

您应该认真考虑在应用程序主线程上执行的任务。主线程是应用程序处理触摸事件和其它用户输入的地方。为了确保应用程序总是可以响应用户,我们不应该在主线程中执行运行时间很长或可能无限等待的任务,比如访问网络的任务。相反,您应该将这些任务放在后台线程。一个推荐的方法是将每个任务都封装在一个操作对象中,然后加入操作队列。当然,您也可以自己创建显式的线程。

将任务转移到后台可以使您的主线程继续处理用户输入,这对于应用程序的启动和退出尤其重要。在这些时候,系统期望您的应用程序及时响应事件。如果应用程序的主线程在启动过程中被阻塞住了,系统甚至可能在启动完成之前将它杀死;如果主线程在退出时被阻塞了,则应用程序可能来不及保存关键用户数据就被杀死了。

有效地使用内存

减少应用程序的内存印迹

表1-7  减少应用程序内存印迹的技巧

技巧

采取的措施

消除内存泄露

由于内存是iPhone OS的关键资源,所以您的应用程序不应该有任何的内存泄露。存在内存泄露意味着应用程序在之后可能没有足够的内存。您可以用Instruments程序来跟踪代码中的泄露,该程序既可以用于仿真器,也可以用于实际的设备。有关如何使用Instruments的更多信息,请参见Instruments用户指南

使资源文件尽可能小

文件驻留在磁盘中,但在使用时需要载入内存。属性列表文件和图像文件是通过简单的处理就可以节省空间的两种资源类型。您可以通过NSPropertyListSerialization类将属性列表文件存储为二进制格式,从而减少它们的使用空间;对于图像,可以将所有图像文件压缩得尽可能小(PNG图像是iPhone应用程序的推荐图像格式,可以用pngcrush工具来进行压缩)。

使用Core Data 或SQLite来处理大的数据集合

如果您的应用程序需要操作大量的结构化数据,请将它存储在Core Data的持久存储或SQLite数据库,而不是使用扁平文件。Core Data和SQLite都提供了管理大量数据的有效方法,不需要将整个数据一次性地载入内存。

Core Data的支持是在iPhone OS 3.0系统上引入的。

延缓装载资源

在真正需要资源文件之前,永远不应该进行装载。预先载入资源文件表面看好象可以节省时间,但实际上会使应用程序很快变慢。此外,如果您最终没有用到那些资源,预先载入将只是浪费内存。

将程序连编为Thumb格式

加入-mthumb开关可以将代码的尺寸减少最多达35%。但是,对于具有大量浮点数运算的代码模块,请务必将这个选项关闭,因为对那样的模块使用Thumb反而会导致性能的下降。

恰当地分配内存

表1-8  分配内存的技巧

技巧

采取的措施

减少自动释放对象的使用

通过autorelease方法释放的对象会留在内存中,直到显式清理自动释放池或者程序再次回到事件循环。在任何可能的时候,请避免使用autorelease方法,而是通过release方法立即收回对象占用的空间。如果您必须创建一定数量的自动释放对象,则请创建局部的自动释放池,以便在返回事件循环之前定期对其进行清理,回收那些对象的内存。

为资源设置尺寸限制

避免装载大的资源文件,如果有更小的文件可用的话。请用适合于iPhone OS设备的恰当尺寸图像来代替高清晰度的图像。如果您必须使用大的资源文件,需要考虑仅装载当前需要的部分。举例来说,您可以通过mmapmunmap函数来将文件的一部分载入内存或从内存卸载,而不是操作整个文件。有关如何将文件映射到内存的更多信息,请参见文件系统性能指南

避免无边界的问题集

无边界的问题集可能需要计算任意大量的数据。如果该集合需要的内存比当前系统能提供的还要多,则您的应用程序可能无法进行计算。您的应用程序应该尽可能避免处理这样的集合,而将它们转化为内存使用极限已知的问题。

浮点数学运算的考虑

减少电力消耗

您可以通过优化如下组件的使用来提高电池的寿命:

  • CPU

  • Wi-Fi和基带(EDGE, 3G)无线信号

  • Core Location框架

  • 加速计

  • 磁盘

您的优化目标应该是以尽可能有效的方式完成大多数的工作。您应该总是采用Instruments和Shark工具对应用程序的算法进行优化。但是,很重要的一点是,即使最优化的算法也可能对设备的电池寿命造成负面的影响。


您向网络传递的数据越多,就需要越多的电能来进行无线发射。事实上,访问网络是您所能进行的最耗电的操作

 

 

抱歉!评论已关闭.