OC中多线程编程的核心就是block与gcd。这虽然是两种不同的技术,但他们是一并引入的。block是一种可在C、C++及OC代码中使用的“词法闭包”(lexical closure),它极为有用,借此机制,开发者可将代码像对象一样传递,令其在不同环境(context)下运行。在block的范围内,它可以访问到其中的全部变量。
gcd是一种与block有关的技术,它提供了对线程的抽象,而这种抽象基于“派发队列”(dispatch queue)。开发者可将block排入队列中,有gcd负责处理所有调度事宜。gcd会根据系统资源情况,适时得创建、复用、摧毁后台线程,以便处理每个队列。此外,使用GCD还可以方便的完成常见编程任务,比如编写“只执行一次的线程安全代码”(thread-safe single-code execution),或者根据可用的系统资源来并发执行多个操作。
block和gcd是当前OC的编程基石。因此必须理解其工作原理及功能
第41条:多用派发队列,少用同步锁
在OC中,如果有多个线程要执行同一份代码,那么有时候可能会出问题。一般采用锁来实现某种同步机制。在GCD出现之前,第一种是采用“同步Block”,另一个办法是使用NSLock对象。这两种都会遇到死锁现象。使用gcd,它能以简单、高效的形式为代码枷锁。
“串行同步队列”(serial synchronization queue)。将读取/写入都安排在同一个队列里,即可保证数据同步。
_syncQueue = dispatch_queue_create("com.effective-c.syncQueue",NULL);
-(NSString*) someString {
__block NSString* localSomething;
dispatch_sync(_syncQueue,^{
localSomeString = _someString;
});
return localSomeString
}
-(void) setSomeString:(NSString*) someString{
dispatch_sync(_syncQueue,^{
_someString = someString;
});
}
此模式的思路是:把设置操作与获取操作都安排在序列化的队列里执行,这样的话,所有的针对属性的访问都是同步了。这点很想symbian的多动对象(OA)
读、写操作。前者可以并发读取,但是后者不能并发。可以优化代码如下:
_syncQueue = dispatch_get_global_create("DISPATCH_QUEUE_PRIORITY_DEFAULT",NULL);
-(NSString*) someString {
__block NSString* localSomething;
dispatch_sync(_syncQueue,^{
localSomeString = _someString;
});
return localSomeString
}
-(void) setSomeString:(NSString*) someString{
dispatch_barrier_async(_syncQueue,^{
_someString = someString;
});
}
其中dispatch_barrier_async是栅栏,在队列中,栅栏块必须单独执行,不能与其他块并行。如图所示:
第42条:多用gcd,少用performSelector系列方法
【本节要点】
● performSelector系列方法存在内存管理方面容易有疏忽。它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。
● performSelector系列方法能处理的选择子太过局限了,选择子的返回值类型及发送给方法的参数个数都有限制。
● 如果想把任务放到另一个线程上执行,那么最好不要用performSelector系列方法,而是使用gcd实现。
第43条:掌握gcd及操作队列的使用时机
很少有其他技术能与gcd的同步机制相媲美。对于那些只需要执行一次的代码来说,也是如此,使用gcd的dispatch_once最为方便。然而在执行后台任务时候,gcd并不一定是最佳方式。还有一种技术叫做NSOpertationQueue,它虽然与gcd不同,但是却与之相关,开发者可以把操作以NSOpertation子类的形式放到队列中,这些操作也能够并发执行。
在两者差别中,首先要注意:gcd是纯c的API,而操作队列是OC对象。gcd处理轻量级block而操作队列处理重量级OC对象。
用NSOperationQueue类的addOperatonWithBlock方法搭配NSBlockOperation类来操作队列,其语法与纯gcd方法非常类似。使用NSOperation和NSOpertaionQueue的好处如下:
● 取消某个操作
● 指定操作间的依赖关系
● 通过键值观测机制监控NSOperation属性。
● 指定操作的优先级
● 重用NSOperation对象。
操作队列很多地方胜过派发队列。操作队列提供了很多执行任务的方法,而且都是写好了,直接就能用。开发者不用再编写复杂的调度器。
NSNotificationCenter使用了操作队列
本节要点
● 在解决多线程与任务管理问题时,派发队列并非唯一方案。
● 操作队列提供了一套高层次的OC API。能实现纯gcd所具备的绝大部分功能,而且还能完成一些更为复杂的操作,那些操作如果该用gcd来实现,则需要另外写代码。
第44条:通过dispatch group机制,根据系统资源状况来执行任务
第45条:使用dispatch_once来执行只需运行一次的线程安全代码
直接看代码吧,通常是单例的实现。
+(id) sharedInstance{
static EOCClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{sharedInstance = [[self alloc]init];});
return sharedInstance;
}
第46条:不要使用dispatch_get_current_queue
-(NSString*) someString {
__block NSString* localSomething;
dispatch_sync(_syncQueue,^{
localSomeString = _someString;
});
return localSomeString
}
-(void) setSomeString:(NSString*) someString{
dispatch_sync(_syncQueue,^{
_someString = someString;
});
}