一、在后台工作量少
当用户没有主动使用你的应用程序时,系统会将其转换为背景状态。该系统最终可能会暂停您的应用程序,如果它不执行重要的工作,如整理用户启动一个任务或在一个特别声明后台执行模式下运行。
重要
您的应用程序不应该等待被系统暂停。它应开始逐步结束活动后立即通知状态已更改。当您的应用程序完成剩余的任务,应通知系统后台活动完成。如果不这样做会导致应用程序保持活跃和不必要的汲取能量。
通过后台应用能源浪费的常见原因
应用程序进行不必要的后台活动浪费能源。以下是浪费能源的后台应用程序的一些常见原因:
不通知系统时背景活动完成
播放音频无声
执行位置更新
与蓝牙配件交互
可能推迟下载
暂停活动时,您的应用程序不被激活或移动到背景
实现UIApplicationDelegate在您的应用程序委托方法接听电话并暂停活动时,您的应用程序不被激活或从前景到背景的过渡。
applicationWillResignActive
在applicationWillResignActive:当你的应用程序进入非激活状态时,如电话或短信进来,或者用户切换到另一个应用程序,你的应用程序开始过渡到后台状态的方法被调用。这是暂停活动,保存数据,并可能暂停准备好去处。请参阅清单3-1。
OBJECTIVE-C的
-(void)applicationWillResignActive:(UIApplication*)application{
// Halt operations, animations, and UI updates
}
迅速
optionalfuncapplicationWillResignActive(_application:UIApplication) {
// Halt operations, animations, and UI updates
})
重要
您的应用程序不应该依赖于状态转换为契机,以保存数据。它应该在整个应用程序生命周期保存在适当的点数据,以防止数据丢失。
applicationDidEnterBackground
该applicationDidEnterBackground:方法被称为你的应用程序进入后台状态之后。停止任何操作,动画和UI将立即更新。见列表3-2。
OBJECTIVE-C的
-(void)applicationDidEnterBackground:(UIApplication*)application{
// Halt operations, animations, and UI updates immediately
}
迅速
optionalfuncapplicationDidEnterBackground(_application:UIApplication) {
// Halt operations, animations, and UI updates immediately
})
的iOS只允许为几秒钟applicationDidEnterBackground的方法来运行。如果您的应用程序需要更多的时间来完成必要的执行用户发起的任务,就应该要求更多的后台执行时间的系统中最多可以根据要求了几分钟时间。调用beginBackgroundTaskWithExpirationHandler:方法,并传递给它的处理程序,如果额外的时间耗尽被调用。接下来,运行在一个调度队列或辅助线程余下的任务。
当后台任务完成后,打电话endBackgroundTask:让系统知道处理完成的方法。如果不调用此方法和背景执行时间排气管,然后完成处理程序被调用给你的包裹事情做最后一搏。在此之后,您的应用程序被暂停。请参阅清单3-3。
重要
一旦程序完成后执行后台任务,不用等待系统调用到期处理程序。打电话endBackgroundTask:为您的应用程序完成执行后台任务,尽快。
OBJECTIVE-C的
// Request additional background execution time
UIBackgroundTaskIdentifierbgTaskID=[[UIApplicationsharedApplication]beginBackgroundTaskWithExpirationHandler:^{
// Completion handler to be performed if time runs out
}];
// Initiate background tasks
// Notify the system when the background tasks are done
[[UIApplicationsharedApplication]endBackgroundTask:bgTaskID];
迅速
// Request additional background execution time
varbgTaskID:UIBackgroundTaskIdentifier=0
bgTaskID=UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler() {
// Completion handler to be performed if time runs out
}
// Initiate background tasks
// Notify the system when the background tasks are done
UIApplication.sharedApplication().endBackgroundTask(bgTaskID)
恢复活动时,您的应用程序变得活跃
实现UIApplicationDelegate在您的应用程序委托方法接听电话并恢复活动时,您的应用程序再次激活。
applicationWillEnterForeground
该applicationWillEnterForeground:方法是从后台程序到活动的应用程序您的应用程序转换之前立即调用。开始恢复操作,装载数据,重新初始化用户界面,并让你的应用程序为用户准备。参见清单3-4。
OBJECTIVE-C的
-(void)applicationWillEnterForeground:(UIApplication*)application{
// Prepare to resume operations, animations, and UI updates
}
迅速
optionalfuncapplicationWillEnterForeground(_application:UIApplication) {
// Prepare to resume operations, animations, and UI updates
})
applicationDidBecomeActive
在applicationDidBecomeActive:后您的应用程序将成为活动的应用程序,系统正在启动或从背景或无效状态转换后方法被调用。全面恢复就停止了任何操作。见列表3-5。
OBJECTIVE-C的
-(void)applicationDidBecomeActive:(UIApplication*)application{
// Resume operations, animations, and UI updates
}
迅速
optionalfuncapplicationDidBecomeActive(_application:UIApplication) {
// Resume operations, animations, and UI updates
})
解决失控的后台应用程序崩溃
iOS版采用了CPU的监视器监视后台应用程序的CPU使用率过高,并终止他们,如果他们跌倒的某些限制之外。执行正常后台活动大多数应用程序应该永远不会遇到这种情况。但是,如果您的应用程序达到限制和终止,崩溃日志表明终止的原因。的异常类型EXC_RESOURCE和子类型的CPU_FATAL指定,以表明限制是超出消息一起。见列表3-6。
Exception Type: EXC_RESOURCE
Exception Subtype: CPU_FATAL
Exception Message: (Limit 80%) Observed 89% over 60 seconds
该日志还包括一个堆栈跟踪,它可以让你决定你的应用程序在做什么,它被终止权利之前。通过分析堆栈跟踪,可以识别失控代码的位置,并解决它。
注意
CPU Monitor是在8 iOS版及更高版本。
二、优先工作与服务类质量
应用程序和业务竞争,利用有限的资源,CPU,内存,网络接口等。为了保持响应速度和效率,系统需要优先考虑的任务,并就何时执行这些明智的决定。
工作直接影响到用户,比如用户界面的更新,是非常重要的,并且优先于可在后台发生的其他工作。该较高优先级的工作经常使用更多的能量,因为它可能需要对系统资源的实质和即时访问。
作为一个开发者,你可以帮助系统通过分类你的应用程序的工作,根据重要性更有效的优先次序。即使你已经实现了其他提高效率的措施,如推迟工作,直到一个最佳时间,系统仍然需要优先执行某种程度。因此,它仍然是重要分类您的应用程序执行的工作。
关于服务类质量
一个服务质量(QoS)班的质量让您分类由执行工作NSOperation,NSOperationQueue,NSThread对象,调度队列和pthread的(POSIX线程)。通过分配QoS的工作,你表明它的重要性,系统优先并相应地调度它。例如,系统执行由用户发起的超过可推迟到下一个更优化的时间后台工作越快工作。在一些情况下,系统资源可能从较低优先级的工作重新分配程,给予更高的优先级的工作。
因为较高优先级的工作进行更快速,更以比低优先级的工作更多的资源时,它通常需要比低优先级的工作更多的能量。准确地指定相应的QoS等级的工作您的应用程序进行,确保您的应用程序响应以及高效节能。
注意
服务质量是iOS中8及更高版本。
选择服务类的质量
该系统使用的QoS信息来调整优先级如调度,CPU和I / O吞吐量,和计时器延迟。其结果是,执行的工作保持性能和能量效率之间的平衡。
当您将QoS应用到一个任务,考虑如何影响用户以及它如何影响其他工作。如所示表4-1中,有四个主要的QoS类别,每个对应的工作重要性的水平。
表4-1主QoS类别(按优先顺序显示)
QoS等级
服务质量的工作和重点类型
要执行的工作期限
用户互动
是与用户交互,诸如主线程上操作的,清爽的用户界面,或执行的动画的工作。如果工作没有迅速发生,用户界面可能会出现冰冻。集中在响应速度和性能。
工作几乎是瞬间完成的。
用户启动
工作用户发起并需要立即的效果,例如打开一个保存的文档或当用户点击用户界面的东西执行操作。工作是必需的,以便继续的用户交互。集中在响应速度和性能。
工作几乎是瞬时的,例如在几秒钟或更小。
效用
这可能需要一些时间来完成,并且不需要立即结果,例如下载或导入数据的工作。实用程序任务通常具有进度条是对用户可见。专注于提供反应性,性能和能量效率之间的平衡。
工作需要几秒钟到几分钟。
背景
其操作在后台和不可见的用户,例如索引,同步和备份工作。专注于能源效率。
工作需要显著的时间,比如几分钟或几小时。
重要
理想情况下,在实用的QoS级别运行你的应用程序,或至少低90%的时间,当用户活动没有发生。
服务类特质
除了 主QoS等级,有两种特殊类型的QoS(描述的表4-2)。在大多数情况下,你不会接触到这些类,但仍有在明知其存在的价值。
表4-2特殊的QoS等级
QoS等级
描述
默认
该QoS的优先级用户启动和效用之间下降。该QoS并不旨在由开发者用来分类工作。有没有分配QoS信息工作被视为默认,和GCD全局队列在这个水平上运行。
未指定
这表示没有的QoS信息和线索环境的QoS应推断该系统。如果他们使用传统的API,可以选择螺纹出的QoS线程可以有一个未指定的QoS。
指定的QoS业务和队列
如果应用程序使用操作和队列进行工作,你可以指定该工作的服务质量。NSOperation而NSOperationQueue这两个拥有qualityOfService财产,类型NSQualityOfService,可设置为以下值之一:
NSQualityOfServiceUserInteractive
NSQualityOfServiceUserInitiated
NSQualityOfServiceUtility
NSQualityOfServiceBackground
清单4-1显示了如何设置QoS进行的操作。
OBJECTIVE-C的
NSOperation*myOperation=[[MyOperationalloc]init];
myOperation.qualityOfService=NSQualityOfServiceUtility;
迅速
letmyOperation:NSOperation=MyOperation()
myOperation.qualityOfService= .Utility
服务推理和促进质量
注意,服务质量不适合操作和队列的静态设置,并且可以根据多种标准随时间波动。例如,可能会发生的情况下的动作的QoS和队列的QoS的不匹配,操作和相关的操作不匹配,或操作没有分配的QoS。在这些情况下,一个服务质量可以推断。
许多规则管理的QoS推理和推广如何与有关队列发生(见表4-3)和行动(见表4-4)。
表4-3NSOperationQueueQoS的推断和提升规则
情况
结果
队列没有分配的QoS和与QoS的操作被添加到队列。
队列及它的其他业务,如果有的话,不受影响。
队列分配有QoS和与QoS的操作被添加到队列。
队列的服务质量,如果新的操作的服务质量也较高得到促进。
任何具有较低的QoS队列的业务也有促进作用。
被添加到队列在将来具有较低QoS的任何操作都将推断更高的QoS。
队列的服务质量是通过改变队列的值升高qualityOfService属性。
任何具有较低的QoS队列的业务提升到更高的服务质量。
被添加到队列在将来具有较低QoS的任何操作都将推断更高的QoS。
队列的服务质量是通过改变队列的值降低qualityOfService性能。
任何队列的业务不受影响。
被添加到队列在将来的任何操作都将推断低服务质量,除非它们具有指定较高的QoS,在这种情况下,它们将保留其分配的QoS等级。
表4-4NSOperation推断和提升规则
情况
结果
操作未分配的QoS。
操作推断父操作,队列的QoS的[NSProcessInfo performActivityWithOptions:reason:usingBlock:]块,或线程,如果有的话。
在其中的主线,一个服务质量上创建一个操作的情况NSQualityOfServiceUserInitiated推断。
与QoS的操作被添加到队列具有更高的QoS。
该操作的服务质量被促进以匹配队列的服务质量。
含的操作的队列的服务质量得到促进。
操作推断的队列的新的QoS如果它比操作的当前服务质量水平。
另一种操作变得依赖于操作(父)(子)。
父操作推断孩子操作的服务质量如果服务质量较高。
该操作的服务质量是通过改变操作的凸起qualityOfService属性。
操作推断新的QoS。
任何子操作提升到新的QoS,如果它是更高的。
在操作的队列是在前面的操作其它操作被提升到新的QoS,如果它是高。
操作的服务质量是通过改变操作下调qualityOfService财产。
操作推断新的QoS。
任何子操作不受影响。
操作的队列不受影响。
调整运行的操作的服务质量
一旦操作正在运行,你可以改变它的QoS通过以下方式之一:
更改qualityOfService操作的财产。请注意,这样做也改变了正在运行的操作的线程的QoS。
添加一个新的操作具有更高的QoS运行操作的队列中。这将促进运行的操作的服务质量,以匹配操作的服务质量。
使用addDependency:以具有较高QoS的添加的操作的运转动作作为从属。
使用waitUntilFinished或waitUntilAllOperationsAreFinished。这将促进运行的操作的服务质量,以匹配呼叫者的服务质量。
指定调度队列和块服务质量
如果应用程序使用GCD,QoS等级可以应用到调度队列和块。
调度队列
对于调度队列,您可以通过拨打指定的QoSdispatch_queue_attr_make_with_qos_class创建队列时。首先,创建QoS的调度队列属性,然后提供属性,当您创建队列,如图清单4-2。
OBJECTIVE-C的
dispatch_queue_attr_tqosAttribute=dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT,QOS_CLASS_UTILITY,0);
dispatch_queue_tmyQueue=dispatch_queue_create("com.YourApp.YourQueue",qosAttribute);
迅速
letqosAttribute=dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT,QOS_CLASS_UTILITY,0)
letmyQueue=dispatch_queue_create("com.YourApp.YourQueue",qosAttribute)
表4-5显示了GCD QoS等级如何映射到基金的QoS当量。
表4-5的GCD基金的QoS映射
GCD QoS等级(在sys / qos.h定义)
对应基金的QoS等级
QOS_CLASS_USER_INTERACTIVE
NSQualityOfServiceUserInteractive
QOS_CLASS_USER_INITIATED
NSQualityOfServiceUserInitiated
QOS_CLASS_UTILITY
NSQualityOfServiceUtility
QOS_CLASS_BACKGROUND
NSQualityOfServiceBackground
的QoS是一个调度队列的不可变属性,并且一旦该队列已创建不能改变。要检索已分配给一个调度队列中的服务质量,请致电dispatch_queue_get_qos_class。请参阅清单4-3。
OBJECTIVE-C的
qosClass=dispatch_queue_get_qos_class(myQueue,&relative);
迅速
letqosClass=dispatch_queue_get_qos_class(myQueue, &relative)
全局并发队列
在过去,GCD已用于优先的工作提供了高,缺省情况下,低,和背景全局并发队列。相应的QoS等级应该已经到位,这些队列中。表4-6介绍了这些队列及其相应的QoS等级之间的映射。
表4-6GCD全局并发队列的QoS映射
全球队列
相应的QoS等级
主线程
用户互动
DISPATCH_QUEUE_PRIORITY_HIGH
用户启动
DISPATCH_QUEUE_PRIORITY_DEFAULT
默认
DISPATCH_QUEUE_PRIORITY_LOW
效用
DISPATCH_QUEUE_PRIORITY_BACKGROUND
背景
一个全球性的并发队列存在于每个QoS等级。检索对应于给定的QoS全局并发队列,呼叫dispatch_get_global_queue并传递它期望的QoS类。清单4-4,例如,检索全局并发队列为公用事业QoS等级。
OBJECTIVE-C的
utilityGlobalQueue=dispatch_get_global_queue(QOS_CLASS_UTILITY,0);
迅速
utilityGlobalQueue=dispatch_get_global_queue(QOS_CLASS_UTILITY,0)
没有分配QoS和不针对一个全球性的并发队列推断QoS等级的未指定的队列。
调度块
在GCD块API允许在块级应用的QoS类,例如打电话时为dispatch_async,dispatch_sync,dispatch_after,dispatch_apply,或dispatch_once。你这样做,当你创建块,如图清单4-5。
OBJECTIVE-C的
dispatch_block_tmyBlock;
myBlock=dispatch_block_create_with_qos_class(
0,QOS_CLASS_UTILITY,-8,^{…});
dispatch_async(myQueue,myBlock);
迅速
letblock=dispatch_block_create_with_qos_class(0,QOS_CLASS_UTILITY) {
...
}
dispatch_async(myQueue,myBlock)
优先级倒置
当高优先级的工作变得依赖于低优先级的工作,或成为低优先级工作的结果,优先级反转发生。其结果,可能会发生阻塞,纺纱,和轮询。
在同步工作的情况下,系统将尝试通过提高较低优先级的工作的QoS反演期间自动解决优先级反转。这会发生在以下几种情况:
当dispatch_sync()和dispatch_wait()被要求对串行队列块。
当pthread_mutex_lock()同时互 斥体是由一个线程具有较低的QoS举行之称。在这种情况下,该线程持有锁升高到呼叫者的服务质量。然而,这种服务质量提升不跨多个锁发生。
在异步工作的情况下,系统将试图解决上串行队列发生的优先级倒置。
重要
开发人员应该尽量保证优先级倒置不摆在首位出现,所以该系统不会被迫尝试解决。
指定线程的QoS
NSThread拥有一个qualityOfService类型的属性NSQualityOfService。这个类将不能推断基于其执行的背景下,服务质量,因此线程开始前仅可改变这个属性的值。读出qualityOfService在任何时间一个线程提供其当前值。
主线程和当前的线程
主线程会自动分配基于其环境的QoS。在应用程序中,主线程在用户交互的QoS级别运行。在XPC服务,主线程默认的QoS的运行。要检索主线程的服务质量,调用qos_class_main函数,如在清单4-6。
OBJECTIVE-C的
qosClass=qos_class_main();
迅速
letqosClass=qos_class_main()
检索当前正在运行的线程的服务质量,调用qos_class_self函数,如图4-7。
OBJECTIVE-C的
qosClass=qos_class_self();
迅速
letqosClass=qos_class_self()
pthreads的
您可以通过使用属性创建的pthread时指定QoS等级,如图4-8,它创建了一个实用的pthread。
OBJECTIVE-C的
pthread_attr_tqosAttribute;
pthread_attr_init(&qosAttribute);
pthread_attr_set_qos_class_np(&qosAttribute,QOS_CLASS_UTILITY,0);
pthread_create(&thread,&qosAttribute,f,NULL);
迅速
varthread=pthread_t()
varqosAttribute=pthread_attr_t()
pthread_attr_init(&qosAttribute)
pthread_attr_set_qos_class_np(&qosAttribute,QOS_CLASS_UTILITY,0)
pthread_create(&thread, &qosAttribute,f,nil)
要更改的pthread,通话的服务质量pthread_set_qos_class_self_np,并通过它的新的QoS应用,如图清单4-9。
OBJECTIVE-C的
pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND,0);
迅速
pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND,0)
服务类的调试质量
通过在Xcode中设置断点或暂停您的应用程序测试时,可以检查与调试导航器中的CPU使用率计您的应用程序,以确认申请正在应用类的QoS。
三、尽量减少使用计时器
您可以通过实现高效节能的API,而不是定时器减少应用的能源使用。该NSURLSessionAPI,例如,提供了一个执行出的进程背景URL会话和完成,当他们收到通知的能力。见推迟网络。如果必须使用定时器,有效地使用它们。
计时器成本高
计时器让您安排延迟或定期操作。计时器等待,直到一定的时间间隔已经过去,然后触发,执行特定操作,例如发送消息到它的目标对象中。从空闲状态唤醒系统时,代为CPU和其他系统从他们的低功耗,空闲状态唤醒的能源成本。如果定时器导致系统唤醒,它招致的成本。
应用程序经常使用的定时器不必要的。如果您在应用程序中使用定时器,考虑是否你真的需要他们。例如,一些应用程序使用计时器轮询状态改变时,他们应事件,而不是回应。其他应用程序使用计时器作为同步工具时,他们应该使用信号量或其他锁来实现最大的效益。有些计时器没有合适的超时执行,使他们时,他们不再需要继续射击。不管场景中,如果有许多计时器调用的唤醒,所述能量冲击是高的。
获取事件通知不使用定时器
一些应用程序使用定时器来监视更改文件的内容,网络可用性,以及其他状态变化。定时器防止CPU去或停留在空闲状态,这增加了能量使用和消耗电池功率。
代替使用定时器来监视的事件,可以使用一个更有效的服务,诸如派遣源。请参阅清单5-1。
OBJECTIVE-C的
constchar*myFile=[@"/Path/To/File"fileSystemRepresentation];
intfileDescriptor=open(myFile,O_EVTONLY);
dispatch_queue_tmyQueue=dispatch_get_main_queue();
constuint64_tdispatchFlags=DISPATCH_VNODE_DELETE|DISPATCH_VNODE_WRITE;
dispatch_source_tmySource=dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fileDescriptor,dispatchFlags,myQueue);
dispatch_source_set_event_handler(mySource,^{
[selfcheckForFile];
});
dispatch_resume(mySource);
迅速
letmyFile= @"/Path/To/File"
letfileDescriptor=open(myFile.fileSystemRepresentation,O_EVTONLY)
letmyQueue=dispatch_get_main_queue()
letdispatchFlags=DISPATCH_VNODE_DELETE|DISPATCH_VNODE_WRITE
letmySource=dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fileDescriptor,dispatchFlags,myQueue)
dispatch_source_set_event_handler(mySource) {
self.checkForFile()
}
dispatch_resume(mySource)
使用事件通知只要有可能。系统提供的服务表5-1提供了自己的相应的编码方法以及常见的系统通知的清单。
表5-1获取事件通知系统服务
事件被通知
方法遵循获取事件通知
在描述
更新文件
配置调度来源。
更新系统范围的文件或目录
创建文件系统事件API的事件流。
网络事件
使用苹果推送通知服务。
卓悦使用。
DNS服务发现编程指南和NSNetServices和CFNetServices编程指南
使用GCD工具同步而不是定时器
大中央调度(GCD)提供调度队列,调度信号量,和其他同步功能,比计时器更有效。
代码清单5-2执行一个线程上运行,而另一个线程,并完成处理程序使用计时器定期检查第一个线程的工作是否已经完成。直到在第一线程的工作完成后,usleep在第二螺纹计时器连续地唤醒系统只以确定在第一线程的工作是否已完成。
OBJECTIVE-C的
BOOLworkIsDone=NO;
/* thread one */
voiddoWork(void){
/* wait for network ... */
workIsDone=YES;
}
/* thread two: completion handler *//*** Not Recommended ***/
voidwaitForWorkToFinish(void){
while(!workIsDone){
usleep(100000);/* 100 ms *//*** Not Recommended ***/
}
[WorkControllerworkDidFinish];
}
迅速
varworkIsDone=false
/* thread one */
funcdoWork() {
/* wait for network ... */
workIsDone=true
}
/* thread two: completion handler *//*** Not Recommended ***/
funcwaitForWorkToFinish() {
while(!workIsDone) {
usleep(100000)/* 100 ms *//*** Not Recommended ***/
}
WorkController.workDidFinish()
}
代码清单5-3带有串行调度队列更有效地执行同步。
OBJECTIVE-C的
myQueue=dispatch_queue_create("com.myapp.myq",DISPATCH_QUEUE_SERIAL);
dispatch_block_tblock;
block=dispatch_block_create(0,^{
/* wait for network ... */
});
/* thread one */
voidbeginWork(void){
dispatch_async(myQueue,block);
};
/* thread two */
voidwaitForWorkToFinish(void){
dispatch_block_wait(block,DISPATCH_TIME_FOREVER);
Block_release(block);
[WorkControllerworkDidFinish];
};
迅速
letmyQueue=dispatch_queue_create("com.myapp.myq",DISPATCH_QUEUE_SERIAL)
letblock=dispatch_block_create(0) {
/* wait for network ... */
}
/* thread one */
funcbeginWork() {
dispatch_async(myQueue,block)
}
/* thread two */
funcwaitForWorkToFinish() {
dispatch_block_wait(block,DISPATCH_TIME_FOREVER)
WorkController.workDidFinish()
}
如果没有不断的唤醒系统,上线完成方法二等待第一个线程的工作结束。
同样,在代码清单5-4演示了如何在一次长时间运行的操作完成另一个线程上执行长时间运行在一个线程中运行,额外的工作。这种技术可以使用,例如,为了防止您的应用程序的主线程上发生阻塞的工作。
OBJECTIVE-C的
dispatch_async(thread2_queue){
/* do long work */
dispatch_async(thread1_queue){
/* continue with next work */
}
};
迅速
dispatch_async(thread2_queue) {
/* do long work */
dispatch_async(thread1_queue) {
/* continue with next work */
}
}
注意
有关使用调度队列和中央调度等同步功能的更多信息,请参阅并发编程指南及大中央调度(GCD)参考。另请参阅同步在线程编程指南。为了达到最佳的能量效率,分类调度队列由系统优先级。请参见指定调度队列和块服务质量在确定工作优先级与服务类质量。
如果你必须使用一个定时器,使用它高效
游戏和其他图形密集型应用程序通常依赖于计时器启动屏幕或动画更新。许多编程接口延迟流程指定的时间段。任何方法或函数,你传递一个相对或绝对的最后期限可能是一个计时器API。例如:
高级别定时器API包括调度计时器源,CFRunLoopTimerCreate和其他CFRunLoopTimer功能外,NSTimer类和performSelector:withObject:afterDelay:方法。
低级别的定时器API包括功能sleep,usleep,nanosleep,pthread_cond_timedwait,select,poll,kevent,dispatch_after,和dispatch_semaphore_wait。
如果您确定您的应用程序需要一个计时器,按照图纸的能量最少的下列原则:
使用计时器经济通过指定合适的超时。
重复无效计时器时,他们不再需要。
设置公差时,计时器应该解雇。
指定合适的超时
许多功能包括超时参数并退出达到超时时。传递的时间间隔,以这些功能使他们如定时器操作,与定时器的所有能源消耗。
如果应用程序使用了不合适的超时值,即使用可浪费能源。例如,在代码清单5-5,从当前时刻的500纳秒的超时值(DISPATCH_TIME_NOW)被传递给dispatch_semaphore_wait函数。直到信号灯发出信号,代码不执行工作的同时dispatch_semaphore_wait不断地超时。
如果应用程序使用的DISPATCH_TIME_FOREVER常数,即使用可以无限期阻止功能,允许在需要时的功能,才恢复。代码清单5-6传递DISPATCH_TIME_FOREVER常数dispatch_semaphore_wait。功能块,直到它收到信号。
OBJECTIVE-C的
while(YES){
dispatch_time_ttimeout=dispatch_time(DISPATCH_TIME_NOW,500*NSEC_PER_SEC);
longsemaphoreReturnValue=dispatch_semaphore_wait(mySemaphore,timeout);
if(havePendingWork){
[selfdoPendingWork];
}
}
迅速
repeat{
lettimeout=dispatch_time(DISPATCH_TIME_NOW,500*Double(NSEC_PER_SEC))
letsemaphoreReturnValue=dispatch_semaphore_wait(mySemaphore,timeout)
if(havePendingWork) {
self.doPendingWork()
}
}whiletrue
OBJECTIVE-C的
while(YES){
dispatch_time_ttimeout=DISPATCH_TIME_FOREVER;
longsemaphoreReturnValue=dispatch_semaphore_wait(mySemaphore,timeout);
if(havePendingWork){
[selfdoPendingWork];
}
}
迅速
repeat{
lettimeout=DISPATCH_TIME_FOREVER
letsemaphoreReturnValue=dispatch_semaphore_wait(mySemaphore,timeout)
if(havePendingWork) {
self.doPendingWork()
}
}whiletrue
在大多数情况下,无限期地阻断(如在清单5-6)比指定时间值更适合。但是,如果你的应用程序确实需要等待超时,指定代表一个有意义的状态变化,比如错误条件或网络超时信号量的值。
无效重复计时器不再需要
如果使用重复计时器,废止或取消它时,你不再需要它。忘记停止计时器浪费大量的能源,而且是解决最简单的问题之一。
代码清单5-7使用重复NSTimer计时器。当不再需要计时器,代码调用invalidate方法再次射击,避免不必要的能源消耗停止计时器。
OBJECTIVE-C的
NSTimer*myTimer=[[NSTimeralloc]initWithFireDate:date
interval:1.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
/* Do work until the timer is no longer needed */
[myTimerinvalidate];/* Recommended */
迅速
varmyTimer=NSTimer.initWithFireDate(date,interval:1.0,target:self,selector:"timerFired:",userInfo:nilrepeats:true)
/* Do work until the timer is no longer needed */
myTimer.invalidate()/* Recommended */
对于重复调度计时器,使用dispatch_source_cancel功能取消计时器,当它不再需要。对于重复CFRunLoop计时,可以使用CFRunLoopTimerInvalidate的功能。
指定配料定时器全系统一公差
指定当你的计时器射击精度公差。系统将使用这种灵活性由少量到定时器的执行转向的时间内它们的公差-使多个定时器可以同时被执行。使用这种方法显着提高的时间,而用户检测系统响应速度没有变化,该处理器花费空转量。
您可以使用setTolerance:方法来指定容忍你的定时器,如图清单5-8。百分之十的耐受性通过设置setTolerance:0.3比较interval:3.0。
OBJECTIVE-C的
[myTimersetTolerace:0.3];
[[NSRunLoopcurrentRunLoop]addTimer:myTimerforMode:NSDefaultRunLoopMode];
迅速
myTimer.tolerance(0.3)
NSRunLoop.currentRunLoop().addTimer(myTimer,forMode:NSDefaultRunLoopMode)
在该示例清单5-9显示了如何使用的最后一个参数设置百分之十的耐受性dispatch_source_set_timer的功能。
OBJECTIVE-C的
dispatch_source_tmyDispatchSourceTimer=dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0,myQueue);
dispatch_source_set_timer(myDispatchSourceTimer,DISPATCH_TIME_NOW,1*NSEC_PER_SEC,NSEC_PER_SEC/10);
dispatch_source_set_event_handler(myDispatchSourceTimer,^{
[selftimerFired];
}
);
dispatch_resume(myDispatchSourceTimer);
迅速
letmyDispatchSourceTimer=dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0,myQueue)
dispatch_source_set_timer(myDispatchSourceTimer,DISPATCH_TIME_NOW,1*Double(NSEC_PER_SEC),Double(NSEC_PER_SEC) /10)
dispatch_source_set_event_handler(myDispatchSourceTimer) {
self.timerFired()
}
dispatch_resume(myDispatchSourceTimer)
可以指定的定时器时间间隔的百分之十的公差CFRunLoop使用定时器CFRunLoopTimerSetTolerance功能,如图清单5-10。百分之十的公差是由第二个参数来设置CFRunLoopTimerSetTolerance,用第三个参数进行比较CFRunLoopTimerCreate。
OBJECTIVE-C的
CFRunLoopTimerRefmyRunLoopTimer=CFRunLoopTimerCreate(kCFAllocatorDefault,fireDate,2.0,0,&timerFired,NULL);
CFRunLoopTimerSetTolerance(myRunLoopTimer,0.2);
CFRunLoopAddTimer(CFRunLoopGetCurrent(),myRunLoopTimer,kCFRunLoopDefaultMode);
迅速
myRunLoopTimer=CFRunLoopTimerCreate(kCFAllocatorDefault,fireDate,2.0,0,0, &timerFired,NULL)
CFRunLoopTimerSetTolerance(myRunLoopTimer,0.2)
CFRunLoopAddTimer(CFRunLoopGetCurrent(),myRunLoopTimer,kCFRunLoopDefaultMode)
您指定计时器的公差后,可能触发其预定的火灾日期和预定日期火,再加上宽容之间的任何时间。计时器不会在预定的火日期前触发。对于重复的计时器,下火日期始终从原始火日期,以保持正轨未来火次与他们原来的计划进行计算。
一般原则是,以公差设定为间隔的至少百分之十的重复计时器,如在上面的例子。甚至容忍少量的对你的应用程序的能源使用显著积极的影响。
注意
你不能指望一个计时器开火您请求的确切纳秒,甚至没有任何宽容。该系统会尽力满足您的需求,但不能保证确切的发射时间。该系统可以应用公差少量甚至到未给予规定量的定时器,但这种公差通常不足以允许多个定时器到单个唤醒期间触发。
四、最小化I / O
每当您的应用程序执行I / O相关的任务,如写文件数据,它带来的系统出空闲状态的。通过写数据较少,聚集在一起写,使用缓存明智,调度网络交易和整体减少I / O,你可以提高你的应用的能效和性能。
优化文件访问
下面是用于优化您的应用程序文件访问的一些准则:
最大限度地减少数据写入。写只有当他们的内容已更改的文件,和总的变化成单一写只要有可能。避免编写出一个完整的文件,如果只有几个字节已经改变。如果您经常更改的大型文件一小部分,可以考虑使用一个数据库来存储数据,而不是。
避免访问内存过于频繁。如果您的应用程序保存状态信息,使之做到这一点,只有当该状态信息发生变化。批量修改尽可能避免写在频繁的间隔小的变化。
阅读并尽可能将数据写入顺序。在一个文件中跳来跳去需要额外的时间来争取到新的位置。
读取和写入文件较大的数据块只要有可能,记住,一次读取的数据太多可能会导致其他问题。例如,前操作完成读取32 MB的文件的全部内容可能引发的那些内容的寻呼。
对于读取或写入显著的数据,可以考虑使用dispatch_io,它提供了进行文件I / O基于GCD异步API。使用dispatch_io让您在高级别指定的数据需求,因此系统可以优化您的访问。见大中央调度(GCD)参考。
如果数据由被随机访问的结构化内容,将其存储在数据库中,并使用例如,SQLite的或核心数据访问它。使用数据库如果数据你操纵量可能增长到超过几兆更是特别重要。见SQLite的软件库和核心数据编程指南。
了解系统如何缓存文件数据,并知道如何善用这些高速缓存。自己避免缓存数据,除非你打算参考不止一次。见该系统具有它自己的文件缓存机制的文件系统编程指南。
注意
有关如何识别和修复文件相关的性能问题信息,请参阅性能提示在文件系统编程指南。
五、应对低功耗模式在iPhone
谁希望延长他们的iPhone的电池续航时间的用户可以启用设置下的低功耗模式>电池。在低功耗模式下,iOS设备通过制定一定的节能措施,延长电池寿命。例如,该系统可以:
减少CPU和GPU的性能
暂停自由裁量和背景的活动,其中包括网络
降低屏幕亮度
减少超时自动锁定装置
禁用邮件提取
禁用运动效果
禁用动画壁纸
当电池电量上升到足够的水平再次模式自动禁用。
您的应用程序应该采取更多措施来帮助系统节约能源当低功耗模式是有效的。例如,您的应用可以减少使用动画,较低的帧速率,停止位置更新,禁用同步和备份,等等。
注意
低功耗模式是在运行iOS 9及更高版本的iPhone的功能。
注册电源状态的通知
您的应用程序可以注册,当电源状态的设备更改(低功耗模式启用或禁用)接收通知。这些通知张贴在全球调度队列。见调度队列中并发编程指南。
要注册电源状态的通知,将消息发送addObserver:selector:name:object:到您的应用程序(实例的默认通知中心NSNotificationCenter)。它传递一个选择打电话NSProcessInfoPowerStateDidChangeNotification,如图清单7-1。
一旦程序被告知功率状态改变的,它应该然后查询isLowPowerModeEnabled来确定当前的电源状态。请参阅清单7-2。如果低功耗模式被激活,那么你的应用程序可以采取适当的措施,以减少活动。否则,它可以恢复正常操作。
OBJECTIVE-C的
[[NSNotificationCenterdefaultCenter]addObserver:self
selector:@selector(yourMethodName:)
name:NSProcessInfoPowerStateDidChangeNotification
object:nil];
迅速
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: “yourMethodName:”,
name:NSProcessInfoPowerStateDidChangeNotification,
object:nil
)
确定电源状态
您的应用程序可以通过访问随时查询当前的电源状态isLowPowerModeEnabled的财产NSProcessInfo类,如在清单7-2。此属性包含一个布尔值,表明低功耗模式是否已启用或禁用。
OBJECTIVE-C的
if([[NSProcessInfoprocessInfo]isLowPowerModeEnabled]){
// Low Power Mode is enabled. Start reducing activity to conserve energy.
}else{
// Low Power Mode is not enabled.
};
迅速
ifNSProcessInfo.processInfo().lowPowerModeEnabled{
// Low Power Mode is enabled. Start reducing activity to conserve energy.
}else{
// Low Power Mode is not enabled.
}
注意
如果该设备的电源状态是未知的,或者如果设备不支持低功耗模式,则此属性始终具有的价值NO。
翻译来源自“谷歌翻译”