线程的可选方案


有时候,你不想继承threads或不想自己创建和管理单独的线程。例如,你想要一个定时器每2秒钟调用你的方法,你可能会写一个线程一直循环,然后睡眠2秒,然后调用你的方法。或者你可能想要写代码处理异步请求,比如从网络上下载文件。或者当你的iPhone空闲时,你想要继续进行重量级的计算处理。这些都不太容易实现或者可能会降低性能。我会讨论一些解决的办法。


NSTimer


NSTimer并不承诺会很精确;如果你设置每0.5秒触发一次,实际上定时器触发的时间可能在0.55和0.6秒之间。但是,如果你只是想相对准确的执行周期性任务的话,这是一个很好的机制。


重复和非重复NSTimer比较


你可以执行重复或非重复的定时器。对于重复的定时器来说,定时器会在你指定的时间间隔内触发,不会停止。如果你想停止的话,你需要手工的使定时器无效。对于非重复定时器来说,它只会触发一次,然后会自动的变为无效。对这两种情况,一旦定时器无效了,你不能在重用它;你必须重新创建一个定时器对象。


要创建一个定时器,你可以使用:


+ scheduledTimerWithTimeInterval:target:selector:userInfo: repeats:


或使用:


+ timerWithTimeInterval:target:selector:userInfo:repeats:


第一种方法会创建一个新的定时器,然后添加到当前的run loop中,然后返回定时器对象给你。第二种方法仅仅创建一个定时器对象;你需要自己通过调用[aTimer addTimer: forMethod]来添加到你自己的run loop中。


当时候重复定时器时,如果你要使定时器变成无效的话,你需要调用方法:[aTimer invalidate];



注意:在一个没有run loop的线程中,NSTimer是不能工作的。



异步函数


在很多情况下,异步函数比线程更轻量级。例如,iPhone环境能够从线程池中重用线程来处理异步函数。更进一步,如果你需要处理100个异步函数,OS可能只需要10个线程,因为一个线程能够处理多个异步函数。唯一的问题就是,它比你创建一些线程和一个异步请求来处理他们看起来更复杂。


Listing 6-9 代码块创建了一个异步请求到服务器上,然后合并返回的结果创建一个数据对象。



相比,使用简单的线程和异步函数处理方案:


  1. @autoreleasepool {
           NSData *p_w_picpathData = [NSData dataWithContentsOfURL:p_w_picpathURL];

    }


在某些情况下,你可能需要写更多的代码来获得更好的性能。你仅仅需要确保实际上做的什么将对你有利。


如果你使用异步HTTP请求,你需要运行在一个后台线程中。如果很长一段时间都没有响应,iOS的策略可能会杀掉你的应用。这会使得你的应用给别人一个不好的印象。



注意:如果你同时有很多HTTP调用,你应该考虑创建一个单独的线程来处理异步调用,从而避免在主线程上调用冲突。



Idle-Time Notifications 空闲通知


有些事情你只想在系统空闲的时候做。例如,你想要从iPhone发送反馈到服务器上,而你又不想在其他处理正在进行或其他用户正在和设备交互的时候发送。你只想在用户或设备没事可做的时候发送。如果是你自己的话,这确实是一个很难的事情。不过高兴的是,苹果已经提供了一个函数来处理了。你可以通过NSNotificationQueue 使用 NSPostWhenIdle类型 来post一个通知,像下面的代码:



你可以看到,它就像使用NSNotificationCenter那样来使用;你添加一个对象和方法作为观察者和selector。然后你可以post一个新的通知到队列中,这样方法就会在系统空闲的时候进行处理。


iPhone线程测试


线程的测试在第二章已经讨论过了,所以图6-13只是一个简单的温习。



这个测试并没有提供太多的信息,除了应用中当前线程的状态。这个能够帮助你了解系统是否因为使用太多线程导致负荷过重,或线程挂起和等待太长时间,这是死锁的一个征兆。


总结


在本章,你学到了如何使用多线程来提升你应用的性能。概念实际上是非常简单的:你创建一个新的线程来处理一些计算。但是,为了使得你的应用正确和安全的运行,你需要注意很多细节。你同样需要知道cpu密集型任务和IO密集型任务,来确保什么会导致性能提升,或使得性能下降。你学到了如何用不同的方法创建,管理,运行线程。你同样学到了使用锁的objective-c风格和它的语法,线程同步的问题,这个实际上是非常重要的。