Perform Selector
NSOperation 与NSOperationQueue 是在Mac OS X 10.5/ iPhoneOS 2.0 的时候推出的功能,GCD 则是在Mac OS X 10.6/iOS 4 的时候推出,在这之前,使用NSObject 的performSelector 系列的API 会是处理threading 时比较容易的作法。就像我们之前在Selector 这一章提到的,我们可以使用以下这些API,将某些工作放在指定的Thread 执行:
-performSelectorOnMainThread:withObject:waitUntilDone:modes:
-performSelectorOnMainThread:withObject:waitUntilDone:
-performSelector:onThread:withObject:waitUntilDone:modes:
-performSelector:onThread:withObject:waitUntilDone:
-performSelectorInBackground:withObject:
作为比较早期的API,和GCD/NSOperationQueue 比较起来,这几组API 的最大缺点便在于不会帮你管理应该要建立多少thread,全部都得要自己手动管理。一台机器有其硬件性能的上限,能够开出多少thread 也有其上限,如果我们建立超过硬体效能上限数量的thread,最糟的状况会造成整台机器瘫痪。
假如我们现在有一百份工作要在背景执行,如果我们使用GCD 或是NSOperationQueue,我们可以把这些工作写成block 或是NSOperation,然后丢到GCD 或NSOperationQueue 中,GCD 或NSOperationQueue 会根据目前的硬件性能决定Thread 的数量,假如目前使用的机器只适合建立三条背景thread,那么,就只会建立三条背景thread,然后将这一百份工作分批放在这三条thread 中执行。
但,如果你写了一个回圈,在回圈中呼叫了一百次-performSelectorInBackground:withObject:
,那么,就真的会建立一百个thread!如果我们想要避免这种状况,那么,在使用-performSelectorInBackground:withObject:
时,就有必要自己写一个queue,将工作指派在特定的thread上。那么,还是使用GCD或是NSOperationQueue会比较容易些。
在呼叫-performSelector:onThread:withObject:waitUntilDone:modes:
与-performSelector:onThread:withObject:waitUntilDone:
的时候,我们要特别注意一下传入的NSThread物件。假如我们在thread这个参数传递nil,那么,就会造成程式卡在这一行,而不会继续执行。
另外一个明显的缺点则是,使用这些API 的时候,一次只能够传递一个参数,所以,当你有一件工作想要放在指定的thread 执行的,又必须要传递多个参数的时候,就必须将这些参数包装成NSArray 或NSDictionary 的物件。
如果你不想写一个专属用来传递参数的Class,只想要传递一个NSArray 过去,而你有一些C primitive type 的参数,像是数字、指标或C structure,为了要能够插入到NSArray 中,就得要先转换成对应的NSValue,在我们要执行的selector 中再从NSValue 转换回来,于是会写不少转换用的code。
另外要注意,如果我们要在背景执行一个selector,这个method里头必须要有自己的auto release pool,才能够正确释放auto release物件(关于auto release请参见内存管理Part 1)。要建立auto release pool,可以手动建立NSAutoreleasePool物件,也可以使用@autoreleasepool
关键字,当然用@autoreleasepool
会比较容易一点,而且在启用ARC之后,也会禁止手动建立NSAutoreleasePool,只能使用@autoreleasepool
。像这样:
- (void)backgroundTask{
@autoreleasepool{
// Write your code here.
}
}