第四十二条 多用GCD,少用performSelector系列方法

2017/9/11 posted in  第六章 块与GCD

Objective-C本质上是一门非常动态的语言(参见第11条),NSObject定义了几个方法, 令开发者可以随意调用任何方法。这几个方法可以推迟执行方法调用,也可以指定运行方法所用的线程。这些功能原来很有用,但是在出现了GCD之后,这些功能就尽量不要使用了,尽量用GCD来取代他们。

其中他们又如下几个方法:

-(id)performSelector : (SEL) selector
//可以传两个参数
-(id)performSelector : (SEL) selector
           withObject:(id)objectA
           withObject:(id)objectB
//传一个参数
-(id)performSelector : (SEL) selector
           withObject:(id)object

具体用法如:

[object performSelector: @selector(selectorName)];

[object performSelector: @selector(setValue:)
             withObject: newValue];

上面的用法,会有很多局限,比如在ARC下,会发出警告表示:也许会内存泄露,这是因为编译器并不知道将要的选择子是什么,因此,也就不了解其方法签名及返回值,甚至连是否有返回值都不清楚,而且由于编译器不知道方法名,所以就没办法运用ARC的内存管麵则来判定返回值是不是该释放。鉴于此,ARC采用了比较谨慎的做法,就是不添加释放操作。然而这么
做可能导致内存泄漏,因为方法在返回对象时可能已经将其保留了。

另一个局限性是:返回值只能是void或对象类型。尽管所要执行的 选择子也可以返回void,但是performSelector方法的返回值类型毕竟是id。如果想返回整数或浮点数等类型的值,那么就需要执行一些复杂的转换操作了,而这种转换很容易出错。

第三个局限是:这个方法最多只能传递两个参数,当选择子得到参数超过两个时,只能运用字典来传送数据(但是容易增加开销和造成bug)。

所以我们的解决方法是:我们使用来代替,并且performSelector系列方法都可以使用GCD机制使用块来实现。我们来举几个例子说明:

如果我们想要延后执行某个任务:

//Using performSelector:withObjectrafterDelay: 
[self performSelector:@selector(doSomething)
           withObject:nil 
           afterDelay:5.0];

//Using dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64 t)(5.0 * NSEC PER SEC));

dispatch_after(time, dispatch_get_main—queue(), ^(void){
     [self doSomething];
});

后者想要在主线程执行某个任务:

// Using performSelectorOnMainThread: withObject .-waitUntilDone :
[self performSelectorOnMainThread:@selector(doSomething)
                      withObject:nil 
                      waitUntilDone:NO];
//Using dispatch_async
// (or if waitUntilDone is YES, then dispatchasync) 
dispatch_async(dispatch_get_main_queue(), ^{
        [self doSomething];
});

要点

  • performSelector系列方法在内存管理方面容易有疏失。它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。
  • performSelector系列方法所能处理的选择子太过局限了,选择子的返回值类型及发送给方法的参数个数都受到限制。
  • 如果想把任务放在另一个线程上执行,那么最好不要用performSelector系列方法,而是应该把任务封装到块里,然后调用大中枢派发机制的相关方法来实现。