iPhone OS编程指南(二)

应用程序图标和启动图像

显示在用户Home屏幕上的图标文件的缺省文件名为Icon.png(虽然通过Info.plist文件中的CFBundleIconFile属性可以进行重命名)。它应该是一个位于程序包最上层目录的PNG文件。应用程序图标应该是一个57x57像素的图像,不带任何刨光和圆角斜面效果。典型情况下,系统在显示之前会将这些效果应用到图标上。然而,在应用程序的Info.plist文件中加入UIPrerenderedIcon键可以重载这个行为,更多信息请参见表1-3

请注意:如果您以ad-hoc的方式(而不是通过AppStore)将应用程序发布给本地用户,则程序包中还应该包含一个512 x 512像素版本的应用程序图标,命名为iTunesArtwork。在分发您的应用程序时,iTunes需要显示这个文件提供的图标。

应用程序的启动图像文件的文件名为Default.png。这个图像应该和应用程序的初始界面比较相似;系统在应用程序准备好显示用户界面之前显示启动文件,使用户觉得启动速度很快。启动图像也应该是PNG图像文件,位于应用程序包的顶层目录。如果应用程序是通过URL启动的,则系统会寻找名为Default-scheme.png的启动文件,其中scheme是URL的模式。如果该文件不存在,才选择Default.png文件。

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

请注意:除了程序包顶层目录中的图标和启动图像,您还可以在应用程序中具体语言的工程子目录下包含这些图像文件的本地化版本。更多有关应用程序本地化资源的信息请参见“国际化您的应用程序”部分。

Nib文件

nib文件是一种数据文件,用于存储可在应用程序需要时使用的一些“冻结”的对象。大多数情况下,应用程序使用nib文件来存储构成用户界面的窗口和视图。当您将nib文件载入应用程序时,nib装载代码会将文件中的内容转化为应用程序可以操作的真正对象。通过这个机制,nib文件省去了用代码创建那些对象的工作。

InterfaceBuilder是一个可视化的设计环境,您可以用它来创建nib文件。您可以将标准对象(比如UIKit框架中提供的窗口和视图)和Xcode工程中的定制对象放到nib文件中。在InterfaceBuilder中创建视图层次相当简单,只需要对视图对象进行简单拖拽就可以了。您也可以通过查看器窗口来配置每个对象的属性,以及通过创建对象间的连接来定义它们在运行时的关系。您所做的改变最终都会作为nib文件的一部分存储到磁盘上。

在运行时,当您需要nib文件中包含的对象时,就将nib文件装载到程序中。典型情况下,装载nib文件的时机是当用户界面发生变化和需要在屏幕上显示某些新视图的时候。如果您的应用程序使用视图控制器,则视图控制器会自动处理nib文件的装载过程,当然,您也可以通过NSBundle类的方法自行装载。

有关如何设计应用程序用户界面的更多信息,请参见iPhone用户界面指南。有关如何创建nib文件的信息则参见InterfaceBuilder用户指南

处理关键的应用程序任务

本部分将描述几个所有iPhone应用程序都应该处理的任务。这些任务是整个应用程序生命周期的一部分,因此也是将应用程序集成到iPhoneOS系统的重要方面。在最坏的情况下,没有很好地处理其中的某些任务甚至可能会导致应用程序被操作系统终止。

初始化和终止

在初始化和终止过程中,UIApplication类会向应用程序的委托发送恰当的消息,使其执行必要的任务。虽然系统并不要求您的应用程序响应这些消息,但是,几乎所有的iPhone应用程序都应该处理这些消息。初始化是您为应用程序准备用户界面及使其进入初始运行状态的阶段。类似地,在终止阶段,您应该把未保存的数据和关键的应用程序状态写入磁盘。

由于一个iPhone应用程序必须在其它应用程序启动之前退出,所以花在初始化和终止阶段的执行时间要尽可能少。初始化阶段并不适合装载大的、却又不需要马上使用的数据结构。在开始阶段,您的目标应该是尽可能快地显示应用程序的用户界面,最好是使它进入最后一次退出的状态。如果您的应用程序在启动过程中需要更多的时间来装载网络数据,或者执行一些可能很慢的任务,则应该首先显示出用户界面并运行起来,然后在后台线程中执行速度慢的任务。这样,您就有机会向用户显示进度条和其它反馈信息,指示应用程序正在装载必要的数据,或者正在执行重要的任务。

表1-5列举出UIApplicationDelegate协议定义的方法,您在应用程序委托中需要实现这些协议方法,以处理初始化和终止的事务。表中还列出了您在每个方法中应该执行的关键事务。

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

委托方法

描述

applicationDidFinishLaunching:

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

applicationWillTerminate:

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

响应中断

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

图1-6显示了在电话、SMS信息、或者日历警告到来时发生的事件序列。紧接在图后面的步骤说明更为详细地描述了事件序列的关键点,包括您在响应每个事件时应该做的事项。这个序列并不反映当用户按下Sleep/Wake按键时发生的情景;该场景的事件序列在步骤说明之后的部分进行描述。

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

The flow of events during an interruption
  1. 系统检测到有电话、SMS信息、或者日历警告发生。

  2. 系统调用应用程序委托applicationWillResignActive:方法,同时禁止将触摸事件发送给您的应用程序。

    中断会导致应用程序暂时失去控制权。如果控制权的丢失会影响程序的行为或导致不好的用户体验,您就应该在委托方法中采取恰当的步骤进行规避。举例来说,如果您的程序是个游戏,就应该暂停。您还应该禁用定时器、降低OpenGL的帧率(如果正在使用OpenGL的话),通常还应该使应用程序进行休眠状态。在这休眠状态下,您的应用程序继续运行,但是不应该做任何重要的工作。

  3. 系统显示一个带有事件信息的警告窗口。用户可以选择忽略或响应该事件。

  4. 如果用户忽略该事件,系统就调用应用程序委托的applicationDidBecomeActive:方法,并重新开始向应用程序传递触摸事件。 

    您可以在这个方法中重新激活定时器、提高OpenGL的帧率、以及将应用程序从休眠状态唤醒。对于处于暂停状态的游戏,您应该考虑使它停在当时的状态上,等待用户做好重新玩的准备。举例来说,您可以显示一个警告窗口,而窗口中带有重新开始的控件。

  5. 如果用户选择响应该事件(而不是忽略),则系统会调用应用程序委托的applicationWillTerminate:方法。您的应用程序应该正常终止,保存所有必要的上下文信息,使应用程序在下一次启动的时候可以回到同样的位置。

    在您的应用程序终止之后,系统就开始启动负责中断的应用程序。

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

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

 

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

观察低内存警告

当系统向您的应用程序发送低内存警告时,您需要加以注意。当可用内存的数量降低到安全阈值以下时,iPhoneOS会通知最前面的应用程序。如果您的应用程序收到这种警告,就必须尽可能多地释放内存,即释放不再需要的对象或清理易于在稍后进行重建的缓存。

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

一旦收到上述的任何警告,您的处理代码就应该立即响应,释放所有不需要的内存。视图控制器应该清除当前离屏的视图对象,您的应用程序委托则应该释放尽可能多的数据结构,或者通知其它应用程序对象释放其拥有的内存。

如果您的定制对象知道一些可清理的资源,则可以让该对象注册UIApplicationDidReceiveMemoryWarningNotification通告,并在通告处理器代码中直接释放那些资源。如果您通过少数对象来管理大多数可清理的资源,且适合清理所有的这些资源,则同样可以让这些对象进行注册。但是,如果您有很多可清理的对象,或者仅希望释放这些对象的一个子集,则在您的应用程序委托中进行释放可能更好一些。

重要提示:和系统的应用程序一样,您的应用程序总是需要处理低内存警告,即使在测试过程中没有收到那些警告,也一样要进行处理。系统在处理请求时会消耗少量的内存。在检测到低内存的情况时,系统会将低内存警告发送给所有正在运行的进程(包括您的应用程序),而且可能终止某些后台程序(如果必要的话),以减轻内存的压力。如果释放后内存仍然不够—可能因为您的应用程序发生泄露或消耗太多内存—系统仍然可能会终止您的应用程序。

 

 

定制应用程序的行为

有几种方法可以对基本的应用程序行为进行定制,以提供您希望的用户体验。本文的下面部分将描述一些必须在应用程序级别进行的定制。

以景观模式启动

为了配合Home屏幕的方向,iPhoneOS的应用程序通常以肖像模式启动。如果您的应用程序既可以以景观模式运行,也可以以肖像模式运行,那么,一开始应该总是以纵向模式启动,然后由视图控制器根据设备的方向旋转用户界面。但是,如果您的应用程序只能以景观模式启动,则必须执行下面的步骤,使它一开始就以景观模式启动。

重要提示:上面描述的步骤假定您的应用程序使用视图控制器来管理视图层次。视图控制器为处理方向改变和复杂的视图相关事件提供了大量的基础设施。如果您的应用程序不使用视图控制器—游戏和其它基于OpenGLES的应用程序可能是这样的—就必须根据需要旋转绘图表面(或者调整绘图命令),以便将您的内容以景观模式展示出来。

 

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

请注意:在v2.1之前的iPhoneOS系统中,如果要以景观模式启动基于视图控制器的应用程序,需要在上文描述的所有步骤的基础上对应用程序根视图的转换矩阵进行一个90度的旋转。在iPhoneOS 2.1之前,视图控制器并不会根据UIInterfaceOrientation键的值自动进行旋转,当然在iPhoneOS 2.1及更高版本的系统中不需要这个步骤。

和其它应用程序进行通讯

如果一个应用程序支持一些已知类型的URL,您就可以通过对应的URL模式和该程序进行通讯。然而,在大多数情况下,URL只是用于简单地启动一个应用程序并显示一些和调用方有关的信息。举例来说,对于一个用于管理地址信息的应用程序,您就可以在发送给它的URL中包含一个Maps程序可以处理的地址,以便显示相应的位置。这个级别的通讯为用户创造一个集成度高得多的环境,减少应用程序重新实现设备上其它程序已经实现的功能的必要性。

苹果内置支持httpmailtotel、和sms这些URL模式,还支持基于http的、指向Maps、YouTube、和iPod程序的URL。应用程序也可以自己注册定制的URL模式。您的应用程序可以和其它应用程序通讯,具体方法是用正确格式的内容创建一个NSURL对象,然后将它传给共享UIApplication对象openURL:方法。openURL:方法会启动注册接收该URL类型的应用程序,并将URL传给它。当用户最终退出该应用程序时,系统通常会重新启动您的应用程序,但并不总是这样。系统会考虑用户在URL处理程序中的动作及在用户看来返回您的应用程序是否合理,然后做出决定。

下面的代码片断展示了一个程序如何请求另一个程序提供的服务(假定这个例子中的“todolist”是由应用程序注册的定制模式):

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

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

 

如果您的应用程序定义了自己的URL模式,则应该实现对该模式进行处理的方法,具体信息在“实现定制的URL模式”部分中进行描述。有关系统支持的URL处理,包括如何处理URL的格式,请参见苹果的URL模式参考

实现定制的URL模式

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

注册定制的URL模式

在为您的应用程序注册URL类型时,必须指定CFBundleURLTypes属性的子属性,我们已经在“信息属性列表”部分中介绍过这个属性了。CFBundleURLTypes属性是应用程序的Info.plist文件中的一个字典数组,每个字典负责定义一个应用程序支持的URL类型。表1-6描述了CFBundleURLTypes字典的键和值。

表1-6   CFBundleURLTypes属性的键和值

CFBundleURLName

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

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

CFBundleURLSchemes

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

图1-7显示了一个正在用内置的Xcode编辑器编辑的Info.plist文件。在这个图中,左列中的URL类型入口相当于您直接加入到Info.plist文件的CFBundleURLTypes键。类似地,“URLidentifier”和“URL Schemes”入口相当于CFBundleURLNameCFBundleURLSchemes键。

图1-7  Info.plist文件中定义一个定制的URL模式

Defining a custom URL scheme in the Info.plist file

您在对CFBundleURLTypes属性进行定义,从而注册带有定制模式的URL类型之后,可以通过下面的方式来进行测试:

  1. 连编、安装、和运行您的应用程序。

  2. 回到Home屏幕,启动Safari(在iPhone仿真器上,在菜单上选择Hardware >Home命令就可以回到Home屏幕)。

  3. 在Safari的地址栏中,键入使用定制模式的URL。

  4. 确认您的应用程序是否启动,以及应用程序委托是否收到application:handleOpenURL:消息。

处理URL请求

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

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

在程序清单1-2显示的application:handleOpenURL:方法实现中,传入的URL对象在其请求和片断部分带有具体应用程序的信息。应用程序委托抽出这些信息—在这个例子中,是指一个to-do任务的名称和到期日—并根据这些信息创建应用程序的模型对象。

程序清单1-2  处理基于定制模式的URL请求

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    if ([[url scheme] isEqualToString:@"todolist"]) {
        ToDoItem *item = [[ToDoItem alloc] init];
        NSString *taskName = [url query];
        if (!taskName || ![self isValidTaskString:taskName]) { // must have a task name
            [item release];
            return NO;
        }
        taskName = [taskName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 
        item.toDoTask = taskName;
        NSString *dateString = [url fragment];
        if (!dateString || [dateString isEqualToString:@"today"]) {
            item.dateDue = [NSDate date];
        } else {
            if (![self isValidDateString:dateString]) {
                [item release];
                return NO;
            }
            // format: yyyymmddhhmm (24-hour clock)
            NSString *curStr = [dateString substringWithRange:NSMakeRange(0, 4)];
            NSInteger yeardigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(4, 2)];
            NSInteger monthdigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(6, 2)];
            NSInteger daydigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(8, 2)];
            NSInteger hourdigit = [curStr integerValue];
            curStr = [dateString substringWithRange:NSMakeRange(10, 2)];
            NSInteger minutedigit = [curStr integerValue];
 
            NSDateComponents *dateComps = [[NSDateComponents alloc] init];
            [dateComps setYear:yeardigit];
            [dateComps setMonth:monthdigit];
            [dateComps setDay:daydigit];
            [dateComps setHour:hourdigit];
            [dateComps setMinute:minutedigit];
            NSCalendar *calendar = [NSCalendar currentCalendar];
            NSDate *itemDate = [calendar dateFromComponents:dateComps];
            if (!itemDate) {
                [dateComps release];
                [item release];
                return NO;
            }
            item.dateDue = itemDate;
            [dateComps release];
        }
 
        [(NSMutableArray *)self.list addObject:item];
        [item release];
        return YES;
    }
    return NO;
}

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

显示应用程序的偏好设置

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

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

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

在确定一组偏好设置是否为程序的必需部分时,请考虑您为程序设计的使用模式。如果您希望用户相对频繁地修改偏好设置,或者这些偏好设置对程序的行为具有相对重要的影响,则可能就是必需部分。举例来说,游戏中的设置通常都是玩游戏的必需部分,或者是用户希望快速改变的项目。然而,由于Settings程序是一个独立的程序,所以只能用于处理用户不频繁访问的偏好设置。

如果您选择在应用程序内进行偏好设置管理,则可以自行定义用户界面及编写代码来实现。但是,如果您选择使用Settings程序,则必须提供一个设置包(SettingsBundle)来进行管理。

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

如果您通过设置包来实现偏好设置管理,则还应该提供一个定制的图标。Settings程序会在您的应用程序包的最顶层寻找名为Icon-Settings.png的图像文件,并将该图像显示在应用程序名称的边上。该文件应该是一个29x29像素的PNG图像文件。如果您没有在应用程序包的最顶层提供这个文件,则Settings程序会缺省使用缩放后的应用程序图标(Icon.png)。

有关如何为应用程序创建设置包的更多信息,请参见“应用程序的偏好设置”部分。

关闭屏幕锁定

如果一个基于iPhoneOS的设备在某个特定时间段中没有接收到触摸事件,就会关闭屏幕,并禁用触摸传感器。以这种方式锁定屏幕是省电的重要方法。因此,除非您确实需要在应用程序中避免无意的行为,否则应该总是打开屏幕锁定功能。举例来说,如果您的应用程序不接收屏幕事件,而是使用其它特性(比如加速计)来进行输入,则可能需要禁用屏幕锁定功能。

将共享的UIApplication对象的idleTimerDisabled属性设置为YES,就可以禁止屏幕锁定。请务必在程序不需要禁止屏幕锁定功能时将该属性重置为NO。举例来说,您可能在用户玩游戏的时候禁止了屏幕锁定,但是,当用户处于配置界面或没有处于游戏活跃状态时,应该重新打开这个功能。

国际化您的应用程序

理想情况下,iPhone应用程序显示给用户的文本、图像、和其它内容都应该本地化为多种语言。比如,警告对话框中显示的文本就应该以用户偏好的语言显示。为工程准备特定语言的本地化内容的过程就称为国际化。工程中需要本地化的候选组件包括:

  • 代码生成的文本,包括与具体区域设置有关的日期、时间、和数字格式。

  • 静态文本—比如装载到web视图、用于显示应用程序帮助的HTML文件。

  • 图标(包括您的应用程序图标)及其它包含文本或具体文化意义的图像。

  • 包含发声语言的声音文件。

  • Nib文件

通过Settings程序,用户可以从Language偏好设置视图(参见图1-8)中选择希望在用户界面上看到的语言。您可以访问General设置,然后在International组中找到该视图。

图1-8  语言偏好设置视图

The Language preference view

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

请注意:您也可以使用ISO 639-2语言码,而不一定使用ISO639-1的定义。有关语言和区域代码的信息,请参见国际化编程主题文档中的“语言和地域的指定”部分。

一个lproj文件夹中包含所有指定语言(还可能包含指定地区)的本地化内容。您可以用NSBundle类或CFBundleRef封装类型提供的工具来(在应用程序的lproj文件夹)定位当前选定语言的本地化资源。列表1-3给出一个包含英语(en)本地化内容的目录。

列表1-3  本地化语言子目录的内容

en.lproj/
    InfoPlist.strings
    Localizable.strings
    sign.png

这个例子目录有下面几个项目:

  • InfoPlist.strings文件,包含与Info.plist文件中特定键(比如CFBundleDisplayName)相关联的本地化字符串值。比如,一个英文名称为Battleship的应用程序,其CFBundleDisplayName键在fr.lproj子目录的InfoPlist.strings文件中有如下的入口:

    CFBundleDisplayName = "Cuirassé";
    
  • Localizable.strings文件,包含应用程序代码生成的字符串的本地化版本。

  • 本例子中的sign.png,是一个包含本地化图像的文件。

为了本地化,我们需要国际化代码中的字符串,具体做法是用NSLocalizedString宏来代替字符串。这个宏的定义如下:

NSString *NSLocalizedString(NSString *key, NSString *comment);

第一个参数是一个唯一的键,指向给定lproj文件夹中Localizable.strings文件里的一个本地化字符串;第二个参数是一个注释,说明字符串如何使用,因此可以为翻译人员提供额外的上下文。举例来说,假定您正在设置用户界面中一个标签(UILabel对象)的内容,则下面的代码可以国际化该标签的文本:

label.text = NSLocalizedString(@"City", @"Label for City text field");

然后,您就可以为给定语言创建一个Localizable.strings文件,并将它加入到相应的lproj文件夹中。对于上文例子中的键,该文件中应该有如下入口:

"City" = "Ville";

请注意:另一种方法是在代码中恰当的地方插入NSLocalizedString调用,然后运行genstrings命令行工具。该工具会生成一个Localizable.strings文件的模板,包含每个需要翻译的键和注释。更多有关genstrings的信息,请参见genstrings(1)的man页面。

更多有关国际化的信息,请参见国际化编程主题

 

性能和响应速度的调优

在应用程序开发过程的每一步,您都应该考虑自己所做的设计对应用程序总体性能的影响。由于iPhone和iPodtouch设备的移动本质,iPhone应用程序的操作环境受到更多的限制。本文的下面部分将描述在开发过程中应该考虑哪些因素。

不要阻塞主线程

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

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

更多有关如何使用操作对象和线程的信息,请参见线程编程指南

有效地使用内存

由于iPhoneOS的虚存模型并不包含磁盘交换区空间,所以应用程序在更大程度上受限于可供使用的内存。对内存的大量使用会严重降低系统的性能,可能导致应用程序被终止。因此,在设计阶段,您应该把减少应用程序的内存开销放在较高优先级上。

应用程序的可用内存和相对性能之间有直接的联系。可用内存越少,系统在处理未来的内存请求时就越可能出问题。如果发生这种情况,系统总是先把代码页和其它非易失性资源从内存中移除。但是,这可能只是暂时的修复,特别是当系统在短时间后又再次需要那些资源的时候。相反,您需要尽可能使内存开销最小化,并及时清除自己使用的内存。

本文的下面部分将就如何有效使用内存和在只有少量内存时如何反应方面提供更多的指导。

减少应用程序的内存印迹

表1-7列出一些如何减少应用程序总体内存印迹的技巧。在开始时将内存印迹降低了,随后就可以有更多的空间用于需要操作的数据。

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

技巧

采取的措施

消除内存泄露

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

使资源文件尽可能小

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

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

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

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

延缓装载资源

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

将程序连编为Thumb格式

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

恰当地分配内存

iPhone应用程序使用委托内存模式,因此,您必须显式保持和释放内存。表1-8列出了一些在程序中分配内存的技巧。

表1-8  分配内存的技巧

技巧

采取的措施

减少自动释放对象的使用

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

为资源设置尺寸限制

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

避免无边界的问题集

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

有关如何在iPhone应用程序中分配内存及使用自动释放池的详细信息,请参见Cocoa基本原理指南文档的Cocoa对象部分。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值