适用于iOS的应用程序编程指南(八)

应用间通信

应用程式只能间接与设备上的其他应用进行通信。您可以使用AirDrop与其他应用程序共享文件和数据。您还可以定义自定义网址方案,以便应用程序可以使用URL向应用发送信息。

注意:您也可以使用UIDocumentInteractionController对象或文档选择器在应用程序之间发送文件。有关添加对文档交互控制器的支持的信息,请参阅iOS的文档交互编程主题。Document Interaction Programming Topics for iOS有关使用文档选择器打开文件的信息,请参阅文档选择器编程指南。Document Picker Programming Guide

支持AirDrop

AirDrop可让您与附近的设备共享照片,文档,URL和其他类型的数据。 AirDrop利用对等网络查找附近的设备并连接到它们。

将文件和数据发送到另一个应用程序

要使用AirDrop发送文件和数据,请使用UIActivityViewController对象从您的用户界面中显示活动表。创建此视图控制器时,指定要共享的数据对象。视图控制器仅显示支持指定数据的活动。对于AirDrop,您可以指定图像,字符串,URL和其他几种类型的数据。您还可以传递采用UIActivityItemSource协议的自定义对象。

要显示活动视图控制器,可以使用类似于清单6-1所示的代码。活动视图控制器自动使用指定对象的类型来确定在活动表中显示的活动。您不必明确指定AirDrop活动。但是,您可以使用视图控制器的excludedActivityTypes属性来防止工作表显示特定类型。在iPad上显示活动视图控制器时,必须使用弹出窗口。

清单6-1在iPhone上显示活动表

- (void)displayActivityControllerWithDataObject:(id)obj{

UIActivityViewController * vc =[[UIActivityViewController alloc]

initWithActivityItems:@ [obj] applicationActivities:nil];

[self presentViewController:vcanimated:YES completion:nil];

}

有关使用活动视图控制器的更多信息,请参阅UIActivityViewController类参考UIActivityViewController Class Reference。有关活动及其支持的数据类型的完整列表,请参阅UIActivity类参考。UIActivity Class Reference

接收文件和数据发送到您的应用程序

要使用AirDrop接收发送到您的应用程序的文件,请执行以下操作:

在Xcode中,声明支持您的应用程序能够打开的文档类型。

在你的应用程序委托中,实现应用程序:openURL:sourceApplication:annotation:method。使用该方法接收其他应用程序发送的数据。

您的Xcode项目的“信息”选项卡包含一个“文档类型”部分,用于指定应用程序支持的文档类型。至少必须指定文档类型的名称和表示数据类型的一个或多个UTI。例如,要声明对PNG文件的支持,您将包含public.png作为UTI字符串。iOS使用指定的UTI来确定您的应用是否有资格打开给定的文档。

将符合条件的文档转移到应用程序的容器后,iOS会启动应用程序(如果需要),并调用应用程序:openURL:sourceApplication:annotation:其应用程序委托的方法。如果您的应用程序处于前台,则应使用此方法打开该文件并将其显示给用户。如果您的应用程序在后台,您可能只会确定该文件在那里,以便以后打开它。因为通过AirDrop传输的文件是使用数据保护进行加密的,除非设备当前被解锁,否则无法打开文件。

您的应用程序有权读取和删除它收到的文件,但没有权限写入该文件。如果您打算修改文件,则必须将其移出当前位置才能执行此操作。建议您删除文件的原始版本。

有关在应用程序中支持文档类型的更多信息,请参阅基于文档的iOS应用程序编程指南。Document-Based App Programming Guide for iOS

使用URL方案与Apps通信

URL方案允许您通过您定义的协议与其他应用程序进行通信。要与实现这种方案的应用程序通信,您必须创建一个格式正确的URL,并要求系统将其打开。要实现对自定义方案的支持,您必须声明对该方案的支持,并处理使用该方案的传入URL。

注意:Apple为http,mailto,tel和sms URL方案提供了内置的支持。它还支持针对地图,YouTube和iPod应用程序的基于http的URL。这些方案的处理程序是固定的,不能更改。如果您的网址类型包含与Apple定义的方案相同的方案,则Apple提供的应用程序将会启动,而不是您的应用程序。有关Apple支持的方案的信息,请参阅AppleURL Scheme Reference。Apple URL Scheme Reference

发送一个URL到另一个应用程序

当您要将数据发送到实现自定义URL方案的应用程序时,请创建一个适当格式的URL并调用该应用对象的openURL:方法。openURL:方法使用注册的方案启动该应用,并将您的URL传递给它。在这一点上,控制传递给新的应用程序。

以下代码片段说明了一个应用程序如何请求另一个应用程序的服务(在本示例中为“todolist”是应用程序注册的假设自定义方案):

NSURL * myURL = [NSURL URLWithString:@“todolist://www.acme.com?Quarterly%20Report#200806231300”];

[UIApplication sharedApplication] openURL:myURL];

如果您的应用程序定义了自定义URL方案,那么它应该实现该方案的处理程序,如实现自定义URL方案中所述。有关系统支持的URL方案的详细信息,包括有关如何格式化URL的信息,请参阅AppleURL Scheme Reference。Apple URL Scheme Reference.

实现自定义URL方案

如果您的应用程序可以接收特殊格式的URL,则应该使用系统注册相应的URL方案。应用程序经常使用自定义URL方案向其他应用程序提供服务。例如,“地图”应用程序支持显示特定地图位置的URL。

注册自定义URL方案

要为您的应用注册URL类型,请在应用的Info.plist文件中包含CFBundleURLTypes密钥。CFBundleURLTypes键包含一系列字典,每个字典都定义了应用程序支持的URL方案。表6-1说明了要包含在每个字典中的键和值。

表6-1 CFBundleURLTypes属性的键和值

CFBundleURLName

包含URL方案的抽象名称的字符串。为了确保唯一性,建议您指定反向DNS风格的标识符,例如com.acme.myscheme。

您指定的字符串也用作应用程序的InfoPlist.strings文件中的键。密钥的值是可读取的方案名称。

CFBundleURLSchemes

包含URL方案名称的字符串数组,例如http,mailto,tel和sms。

注意:如果有多个第三方应用程序注册以处理相同的URL方案,则目前没有确定哪个应用程序将被赋予该方案的过程。

处理URL请求

具有自己的自定义URL方案的应用程序必须能够处理传递给它的URL。所有网址都会在启动时或应用程序运行时或后台传递到您的应用程序代理。要处理传入的URL,您的代理应该实现以下方法:

使用应用程序:willFinishLaunchingWithOptions:和应用程序:didFinishLaunchingWithOptions:方法来检索有关URL的信息,并决定是否要打开它。如果任一方法返回NO,您的应用程序的URL处理代码不被调用。

使用应用程序:openURL:sourceApplication:annotation:打开文件的方法。

如果您的应用程序在URL请求到达时未运行,则会将其启动并移动到前台,以便可以打开该URL。您的应用程序的实现:willFinishLaunchingWithOptions:或应用程序:didFinishLaunchingWithOptions:方法应从其选项字典中检索URL,并确定应用程序是否可以打开它。如果可以,返回YES并让您的应用程序:openURL:sourceApplication:annotation:((或application:handleOpenURL :)方法处理URL的实际打开。 (如果您实现这两种方法,两者都必须返回YES才能打开URL)。图6-1显示了要求打开URL的应用程序的修改启动顺序。


图6-1启动应用程序以打开URL

如果您的应用正在运行,但是在后台发生或在URL请求到达时被暂停,则将其移动到前台以打开该URL。 此后不久,系统调用委托人的应用程序:openURL:sourceApplication:annotation:检查URL并将其打开。图6-2显示了将应用程序移动到前台以打开URL的修改过程。


图6-2 唤醒一个后台应用来打开一个URL

注意:支持自定义URL方案的应用程序可以指定在启动应用程序以处理URL时显示不同的启动映像。有关如何指定这些启动映像的详细信息,请参阅打开URL时显示自定义启动映像。Displaying a Custom Launch Image When a URL is Opened.

所有网址都传递到NSURL对象中的应用程序。您可以定义URL的格式,但NSURL类符合RFC1808规范,因此支持大多数URL格式约定。具体来说,该类包括返回由RFC 1808定义的URL的各个部分的方法,包括用户,密码,查询,片段和参数字符串。您的自定义方案的“协议”可以使用这些URL部分来传送各种信息。

在应用程序的实现中:openURL:sourceApplication:annotation:如清单6-2所示,传入的URL对象在其查询和片段部分中传达特定于应用程序的信息。代理提取此信息- 在这种情况下,即将完成任务的名称以及该任务到期的日期 - 并与之建立应用程序的模型对象。此示例假定用户正在使用公历。如果您的应用程序支持非公历日历,则需要相应地设计您的URL方案,并准备好处理代码中的其他日历类型。

- (BOOL)application:(UIApplication *)applicationopenURL:(NSURL *)url

sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{

if ([[url scheme]isEqualToString:@"todolist"]) {

ToDoItem *item =[[ToDoItem alloc] init];

NSString *taskName =[url query];

if (!taskName || ![selfisValidTaskString:taskName]) { // must have a task name

returnNO;

}

taskName = [taskNamestringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

item.toDoTask =taskName;

NSString *dateString =[url fragment];

if (!dateString ||[dateString isEqualToString:@"today"]) {

item.dateDue = [NSDate date];

} else {

if(![self isValidDateString:dateString]) {

return NO;

}

//format: yyyymmddhhmm (24-hour clock)

NSString*curStr = [dateString substringWithRange:NSMakeRange(0, 4)];

NSIntegeryeardigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(4, 2)];

NSIntegermonthdigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(6, 2)];

NSIntegerdaydigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(8, 2)];

NSIntegerhourdigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(10, 2)];

NSIntegerminutedigit = [curStr integerValue];

NSDateComponents *dateComps = [[NSDateComponents alloc] init];

[dateComps setYear:yeardigit];

[dateComps setMonth:monthdigit];

[dateComps setDay:daydigit];

[dateComps setHour:hourdigit];

[dateComps setMinute:minutedigit];

NSCalendar *calendar = [s[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];

NSDate*itemDate = [calendar dateFromComponents:dateComps];

if(!itemDate) {

return NO;

}

item.dateDue = itemDate;

}

[(NSMutableArray*)self.list addObject:item];

return YES;

}

return NO;

}

一定要验证从传递到您的应用程序的URL获得的输入;请参阅“安全编码指南”Validating Input and Interprocess CommunicationSecure Coding Guide中的验证输入和进程间通信,以了解如何避免与URL处理相关的问题。要了解Apple定义的URL方案,请参阅AppleURL Scheme Reference。Apple URL Scheme Reference

打开URL时显示自定义启动映像

支持自定义URL方案的应用程序可以为每个方案提供自定义的启动映像。当系统启动您的应用程序来处理URL并且没有相关快照可用时,它会显示您指定的启动映像。要指定启动映像,请提供名称使用以下命名约定的PNG映像:

<basename>-<url_scheme><other_modifiers>.png

在这个命名约定中,basename表示由应用程序的Info.plist文件中的UILaunchImageFile键指定的基本映像名称。如果不指定自定义基本名称,请使用字符串Default。名称的部分是您的URL方案名称。要为myappURL方案指定一个通用启动映像,您应该在应用程序的捆绑包中添加名为Default-myapp@2x.png的映像文件。 (@ 2x修饰符表示图像用于Retina显示器,如果您的应用程序还支持标准分辨率显示,则还将提供Default-myapp.png图像。)

有关可以在启动映像名称中包含的其他修饰符的信息,请参阅“信息属性列表密钥引用”Information Property List Key Reference.中的UILaunchImageFile名称键的说明。

性能提示

在开发应用程序的每个步骤中,考虑您的设计选择对应用程序整体性能的影响。电源使用和内存消耗是iOS应用程序的重要考虑因素,还有许多其他注意事项。以下部分将介绍在整个开发过程中应考虑的因素。

降低应用程序的功耗

移动设备上的功耗总是一个问题。 iOS中的电源管理系统通过关闭当前未使用的任何硬件功能来节省电力。您可以通过优化使用以下功能来帮助延长电池寿命:

CPU

Wi-Fi,蓝牙和基带(EDGE,3G)无线电

核心位置框架

加速度计

磁盘

您的优化目标应该是以尽可能最有效的方式进行最多的工作。您应该始终使用Instruments优化您的应用程序的算法。但即使最优化的算法仍然可能会对设备的电池寿命产生负面影响。在编写代码时,您应该考虑以下准则:

避免进行需要轮询的工作。轮询阻止CPU进入睡眠状态。而不是轮询,使用NSRunLoop或NSTimer类来根据需要安排工作。

将共享UIApplication对象的idleTimerDisabled属性设置为NO,尽可能。闲置定时器在指定的不活动时间段后关闭设备的屏幕。如果您的应用程序不需要屏幕保持打开,请让系统将其关闭。如果您的应用程序由于屏幕关闭而产生副作用,您应该修改代码以消除副作用,而不必不必要地禁用空闲计时器。

合并工作尽可能最大限度地延长空闲时间。一般来说,一次执行一组计算的能力通常要比在较长时间内以小块执行的方式更少。周期性地做一点点工作需要更频繁地唤醒CPU,并使其进入可以执行任务的状态。

避免频繁访问磁盘。例如,如果您的应用程序将状态信息保存到磁盘,只有当该状态信息发生变化时,才能进行此操作,并尽可能合并更改,以避免以频繁的间隔写入较小的更改。

不要比所需要的绘制图像更快。绘图是一个昂贵的操作,当谈到电量。不要依靠硬件来调节帧速率。仅绘制与应用程序实际需要的帧数一样多。

如果您使用UIAccelerometer类接收常规加速度计事件,则在不需要时将其禁用。类似地,将事件传送的频率设置为适合您需要的最小值。有关更多信息,请参阅UIKit应用程序的事件处理指南。Event Handling Guide for UIKit Apps.

传输到网络的数据越多,运行无线电的功率就越多。实际上,访问网络是您可以执行的功耗最大的操作。您可以按照以下准则减少该时间:

只有在需要的时候才能连接到外部网络服务器,不要轮询这些服务器。

当您必须连接到网络时,传送完成该作业所需的最少数据量。使用紧凑的数据格式,不要包含被忽略的多余的内容。

以突发方式发送数据,而不是随着时间扩散传输数据包。当检测到缺少活动时,系统会关闭Wi-Fi和单元收音机。当它在更长的时间内传输数据时,您的应用程序使用的功率要比在较短时间内传输相同数据量的功率大得多。

当使用NSURLSession类排列多个上传或下载任务时,将这些项目排队在一起,而不是等待一个完成,然后再启动下一个。当这样做最有效时,系统管理自动执行排队的任务。

只要有可能,使用Wi-Fi无线电连接到网络。 Wi-Fi使用更少的功率,并且优于蜂窝无线电。

如果您使用Core Location框架来收集位置数据,请尽快禁用位置更新,并将距离过滤器和精度等级设置为适当的值。核心位置使用可用的GPS,小区和Wi-Fi网络来确定用户的位置。虽然核心位置很难最大限度地减少这些无线电的使用,但设置准确性和过滤器值可使核心位置在不需要的情况下完全关闭硬件。有关详细信息,请参阅位置和地图编程指南。Location and Maps Programming Guide.

仪器应用程序包含几种用于收集电力相关信息的工具。您可以使用这些仪器来收集关于功耗的一般信息,并收集Wi-Fi和蓝牙无线电,GPS接收器,显示器和CPU等硬件的特定测量。您还可以启用设备上的能量诊断日志记录来收集信息。有关使用仪器收集电源相关数据的信息,请参阅“仪器用户指南”Instruments User Guide。有关如何启用设备上的能源诊断日志记录的信息,请参阅Instruments帮助。Instruments Help.

有效地使用内存

鼓励应用程序尽可能少地使用内存,以便系统可以在内存中保留更多的应用程序,或将更多的内存专用于真正需要它的前台应用程序。系统可用内存量与应用程序的相对性能之间存在直接的相关性。较少的可用内存意味着系统更有可能在满足未来内存请求时遇到问题。

为了确保有足够的可用内存可用,您应该最小化应用程序的内存使用情况,并在系统要求您释放内存时做出响应。

观察低内存警告

当系统向您的应用发出低内存警告时,立即做出回应。低内存警告是您删除对不需要的对象的引用的机会。响应这些警告是至关重要的,因为不这样做的应用程序更有可能被终止。该系统使用以下API向您的应用程序提供内存警告:

应用程序委托的applicationDidReceiveMemoryWarning:方法。

你的UIViewController类的didReceiveMemoryWarning方法。

UIApplicationDidReceiveMemoryWarningNotification通知。

调度源类型为DISPATCH_SOURCE_TYPE_MEMORYPRESSURE。这种技术是唯一可以用来区分记忆压力的严重性的技术。

收到任何这些警告后,您的处理程序方法应该立即释放任何不需要的内存。使用警告来清除缓存并释放图像。如果您没有使用大型数据结构,请将这些结构写入磁盘并释放数据的内存副本。

如果您的数据模型包含已知的可清除资源,则可以为UIApplicationDidReceiveMemoryWarningNotification通知提供相应的管理对象注册,并直接删除对其可清除资源的强引用。处理此通知直接避免了通过应用程序代理路由所有内存警告呼叫的需要。

注意:您可以使用iOS Simulator中的模拟内存警告命令在低内存条件下测试应用程序的行为。

减少应用程序的内存占用

从低占有率开始,您可以在以后扩大应用程序的空间。表7-1列出了有关如何减少应用程序整体内存占用的一些提示。

表7-1降低应用程序内存占用空间的技巧

小帖士

采取行动

消除内存泄漏。

由于内存是iOS中的关键资源,因此您的应用程序不应该有内存泄漏。使用仪器应用程序来跟踪代码中的泄漏,无论是在模拟器还是在实际的设备上。有关使用仪器的更多信息,请参阅仪器用户指南。Instruments User Guide

使资源文件尽可能小。

文件驻留在磁盘上,但在使用之前必须加载到内存中。压缩所有图像文件使其尽可能小。 (要压缩PNG图像- iOS应用程序的首选图像格式 - 请使用pngcrush工具。)您可以使用NSPropertyListSerialization类以二进制格式编写属性列表文件。

对于大型数据集使用Core Data或SQLite。

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

负载资源懒惰。

在实际需要之前,您不应该加载资源文件。预取资源文件可能似乎是节省时间的一种方式,但这种做法实际上会减慢您的应用程序的速度。另外,如果你最终不使用资源,加载它浪费内存没有好的目的。

聪明地分配内存

表7-2列出了改善应用程序内存使用情况的提示。

表7-2分配内存的提示

小贴士

采取行动

对资源施加大小限制。

避免在较小的资源文件中加载大型资源文件。不要使用高分辨率图像,而是使用适合iOS设备的尺寸。如果您必须使用大量资源文件,请查找在任何给定时间仅加载所需文件部分的方法。例如,不是将整个文件加载到内存中,而是使用mmap和munmap函数将文件的部分映射到内存中。有关将文件映射到内存的更多信息,请参阅“文件系统性能指南”。

避免无限制的问题集。

无限制的问题集可能需要任意大量的数据进行计算。如果该集合需要比可用内存更多的内存,您的应用程序可能无法完成计算。您的应用程序应尽可能避免此类设置,并处理已知内存限制的问题。

有关ARC和内存管理的详细信息,请参阅Transition to to ARCRelease Notes。Transitioning to ARC Release Notes

调整您的网络代码

iOS中的网络堆栈包括用于通过iOS设备的无线电硬件进行通信的多个接口。主要的编程接口是CFNetwork框架,它构建在CoreFoundation框架中的BSD套接字和不透明类型之上,以与网络实体进行通信。您还可以在Foundation框架中使用NSStream类和在系统的CoreOS层中找到的低级BSD套接字。

有关如何使用CFNetwork框架进行网络通信的信息,请参阅“CFNetwork编程指南”CFNetwork Programming Guide和“CFNetwork框架参考”CFNetwork Framework Reference。有关使用NSStream类的信息,请参阅FoundationFramework Reference。Foundation Framework Reference

高效网络技巧

通过网络实现接收或传输数据的代码是设备上功耗最大的操作之一。最大限度地减少发送或接收数据的时间有助于延长电池寿命。为此,在编写与网络相关的代码时,您应该考虑以下提示:

对于您所控制的协议,将数据格式定义为尽可能的紧凑。

避免使用聊天协议。

无论何时可以以突发方式发送数据包。

蜂窝和Wi-Fi无线电设计为在没有活动时关闭电源。但是,根据收音机,这样做可能需要几秒钟。如果您的应用程序每隔几秒钟发送一小段数据,无线电可能会保持上电状态,即使没有实际执行任何操作,也会继续消耗电力。而不是更频繁地发送少量数据,最好是以更大的间隔发送更大量的数据。

当通过网络进行通信时,数据包可能随时丢失。因此,在编写网络代码时,应该确保在处理故障时尽可能的保持稳定。执行响应网络条件变化的处理程序是完全合理的,但是如果这些处理程序不被称为一致的,则不要感到惊讶。例如,Bonjour网络回调可能不总是立即响应于网络服务的消失而被调用。Bonjour系统服务在收到服务正在消失的通知时立即调用浏览回调,但网络服务可能会在没有通知的情况下消失。如果提供网络服务的设备意外失去网络连接或传输中的通知丢失,则可能会发生这种情况。

使用Wi-Fi

如果您的应用使用Wi-Fi无线电接入网络,则必须通过将UIRequiresPersistentWiFi密钥包含在应用的Info.plist文件中来通知系统。包含此键可以让系统知道如果它检测到任何活动的Wi-Fi热点,它应该显示网络选择对话框。它也让系统知道在应用程序运行时不应该尝试关闭Wi-Fi硬件。

为了防止Wi-Fi硬件使用太多的电源,iOS有一个内置计时器,如果没有正在运行的应用程序通过UIRequiresPersistentWiFi键请求使用它,则在30分钟后完全关闭硬件。如果用户启动包含该键的应用程序,则iOS会在应用程序的生命周期内有效地禁用计时器。然而,一旦该应用程序退出或暂停,系统将重新启用计时器。

注意:请注意,即使UIRequiresPersistentWiFi的值为true,当设备空闲(即屏幕锁定)时,也不起作用。该应用被视为无效,虽然它可能在某些级别上运行,但它没有Wi-Fi连接。

有关UIRequiresPersistentWiFi键和Info.plist文件的键的更多信息,请参阅信息属性列表文件。The Information Property List File

飞行模式警报

如果您的应用程序在设备处于飞行模式时启动,系统可能会显示警报以通知用户该事实。仅当满足以下所有条件时,系统才会显示此警报:

您的应用程序的信息属性列表(Info.plist)文件包含UIRequiresPersistentWiFi键,该键的值设置为true。

当设备当前处于飞行模式时,您的应用会启动。

切换到飞行模式后,设备上的Wi-Fi尚未手动重新启用。

改进文件管理

最小化写入磁盘的数据量。文件操作相对较慢,涉及写入到具有有限寿命的闪存驱动器。帮助您最大限度减少文件相关操作的一些具体技巧包括:

只写文件的更改部分,并可以汇总更改。避免写出整个文件只是为了更改几个字节。

定义文件格式时,将经过频繁修改的内容组合在一起,以最大限度地减少每次需要写入磁盘的块的总数。

如果您的数据由随机访问的结构化内容组成,请将其存储在Core Data持久存储或SQLite数据库中,特别是如果您正在操纵的数据量可能增长到超过几兆字节。

避免将缓存文件写入磁盘。此规则的唯一例外是当您的应用程序退出时,您需要编写状态信息,以便在下次启动时将应用重新设置为相同的状态。

使应用程序备份更有效率

通过iCloud进行无线备份,或用户通过iTunes同步设备。在备份期间,文件将从设备传输到用户的计算机或iCloud帐户。应用程序沙箱中文件的位置确定是否备份和还原这些文件。如果您的应用程序创建许多大型文件,并定期更改,并将其放置在备份的位置,则备份可能会减慢。在编写文件管理代码时,您需要注意这一事实。

应用程序备份最佳做法

您无需以任何方式准备应用程序进行备份和恢复操作。具有活动iCloud帐户的设备在适当的时间将其应用数据备份到iCloud。对于插入计算机的设备,iTunes会对应用程序的数据文件进行增量备份。但是,iCloud和iTunes不会备份以下目录的内容:

<application_Home>/AppName.app

<Application_Data>/Library/Caches

<Application_Data>/tmp

为了防止同步过程花费很长时间,请选择将文件放在应用程序主目录中的位置。存储大文件的应用程序可能会减慢备份到iTunes或iCloud的过程。这些应用程序还可以消耗大量的用户可用存储空间,这可能会鼓励用户删除应用程序或禁用将该应用的数据备份到iCloud。考虑到这一点,您应该按照以下准则存储应用程序数据:

关键数据应存储在 / Documents目录中。关键数据是您的应用无法重新创建的任何数据,例如用户文档和其他用户生成的内容。

支持文件包括应用程序下载或生成的文件,并且您的应用程序可以根据需要重新创建。存储应用程序支持文件的位置取决于当前的iOS版本。

在iOS 5.1及更高版本中,将支持文件存储在/ Library / Application Support目录中,并使用setResourceValue:forKey:error:method将NSURLIsExcludedFromBackupKey属性添加到相应的NSURL对象。(如果您使用的是Core Foundation,请使用CFURLSetResourcePropertyForKey函数将kCFURLIsExcludedFromBackupKey键添加到CFURLRef对象。)应用此属性可防止将文件备份到iTunes或iCloud。如果您有大量的支持文件,您可以将它们存储在自定义子目录中,并将扩展属性应用于目录。

在iOS 5.0及更早版本中,将支持文件存储在/ Library / Caches目录中,以防止它们被备份。如果您定位到iOS 5.0.1,请参阅如何防止文件备份到iCloud和iTunes?How do I prevent files from being backed up to iCloud and iTunes有关如何从备份中排除文件的信息。

缓存数据应存储在 / Library /Caches目录中。您应该放在Caches目录中的文件示例包括(但不限于)数据库缓存文件和可下载内容,例如杂志,报纸和地图应用程序使用的文件。您的应用程序应该能够优雅地处理系统删除缓存数据以释放磁盘空间的情况。

临时数据应存储在 / tmp目录中。临时数据包括您不需要长时间持续的任何数据。记住在完成这些文件时删除这些文件,以便它们不会继续占用用户设备上的空间。

虽然iTunes支持应用程序包本身,但在每次同步操作中都不会这样做。直接从设备购买的应用程序将在该设备下一次与iTunes同步时进行备份。不过,在后续的同步操作中,应用程序不会备份,除非应用程序包本身已更改(例如,应用程序已更新)。

有关如何使用应用程序目录的其他指导,请参阅文件系统编程指南。File System Programming Guide

应用更新期间保存的文件

当用户下载应用程序更新时,iTunes会将更新安装到新的应用程序目录中。然后将用户的数据文件从旧安装移动到新的应用程序目录,然后再删除旧的安装。在更新过程中,保证以下目录中的文件被保留:

<Application_Data>/Documents

<Application_Data>/Library

虽然其他用户目录中的文件也可能会被移动,但是在更新后,您不应该依赖它们存在。

移动主线程

确保限制您在应用程序的主线程上所做的工作类型。主线程是您的应用程序处理触摸事件和其他用户输入的位置。为了确保您的应用程序始终对用户做出响应,您不应该使用主线程来执行长时间运行或潜在的无界任务,例如访问网络的任务。相反,您应该始终将这些任务移到后台线程上。这样做的首选方法是使用GrandCentral Dispatch(GCD)或NSOperation对象异步执行任务。

将任务移动到后台使您的主线程可以自由地继续处理用户输入,这在您的应用程序启动或退出时尤其重要。在这些时期,您的应用程序有望及时响应事件。如果您的应用程序的主线程在启动时被阻止,系统可能会在应用程序启动之前终止应用程序。如果主线程在退出时被阻止,系统可以在应用程序有机会写出关键的用户数据之前,同样可以杀死应用程序。

有关使用GCD,操作对象和线程的更多信息,请参阅并发编程指南。Concurrency Programming Guide

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 119,374评论 16 133
  • 实施特定应用功能的策略 不同的应用程序有不同的需求,但是一些是在许多类型的应用程序常见的行为,以下部分提供有关如何...
    nicedayCoco阅读 919评论 0 0
  • 机会地获取少量内容 需要定期检查新内容的应用程序可以要求系统将其唤醒,以便它们可以启动该内容的提取操作。要支持此模...
    nicedayCoco阅读 141评论 0 0
  • 昨天晚上又熬夜了,早上11点才起床。知道自己要开始“新生活”了,得精气神十足,就去吃了一直想吃的重庆小面,真的好吃...
    娟儿姐的娟儿阅读 78评论 0 0
  • 我习惯于每天写日记,但正儿八经地想写文章给更多的人看是在百度博客上,不过那时只是想让更多的人看我写的东西,而非是真...
    素年仅十阅读 68评论 0 0