《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》--第六章 第43條
(ps:此乃讀書筆記,加深記憶,僅供大家參考)
第43條:掌握GCD及操作隊(duì)列的使用時(shí)機(jī)
GCD技術(shù)確實(shí)很棒,不過有時(shí)候采用標(biāo)準(zhǔn)系統(tǒng)庫的組件,效果會(huì)更好。一定要了解每項(xiàng)技巧的使用時(shí)機(jī)。
很少有其它技術(shù)能與GCD的同步機(jī)制(參見第41條)相媲美。對(duì)于那些只需執(zhí)行一次的代碼來說,也是如此,使用GCD的dispatch_once(參見第45條)最為方便。然而,在執(zhí)行后臺(tái)任務(wù)時(shí),GCD并不一定是最佳方式。還有一種技術(shù)叫做NSOperationQueue,它雖然與GCD不同,但卻與之類似,開發(fā)者可以把操作以NSOperation子類的形式放在隊(duì)列中,而這些操作也能夠并發(fā)執(zhí)行。其與GCD派發(fā)隊(duì)列有相似之處,這并非巧合?!安僮麝?duì)列”(operation queue)在GCD之前就有了,其中有些設(shè)計(jì)原理因操作隊(duì)列而流行,GCD就是基于這些原理構(gòu)建的。實(shí)際上,從iOS4與MAC OSX 10.6開始,操作隊(duì)列在底層是用GCD來實(shí)現(xiàn)的。
在兩者的諸多差別中,首先要注意:GCD是純C的API,而操作隊(duì)列則是Objective-C的對(duì)象。在GCD中,任務(wù)用塊來表示,而塊是個(gè)輕量級(jí)數(shù)據(jù)結(jié)構(gòu)(參見第37條)。與之相反,“操作”(operation)則是個(gè)更為重量級(jí)的Objective—C對(duì)象。
用NSOperationQueue類的“addOperationWithBlock:”方法搭配NSBlockOperation類來使用操作隊(duì)列其語法與純GCD方式非常類似。是用NSOperation及NSOperationQueue的好處如下:
- 取消某個(gè)操作。如果使用操作隊(duì)列,那么想要取消操作隊(duì)列是很容易的。運(yùn)行任務(wù)之前,可以在NSOperation對(duì)象上調(diào)用cancel方法,該方法會(huì)設(shè)置對(duì)象內(nèi)的標(biāo)志位,用以表明此任務(wù)不需執(zhí)行,不過,已經(jīng)啟動(dòng)的任務(wù)無法取消。若是不使用操作隊(duì)列,而是把塊安排到GCD隊(duì)列,那就無法取消了。開發(fā)者可以在應(yīng)用程序?qū)幼约簛韺?shí)現(xiàn)取消功能,不過這樣做需要編寫很多代碼,而那些代碼其實(shí)已經(jīng)由操作隊(duì)列實(shí)現(xiàn)好了。
- 指定操作間的依賴關(guān)系。一個(gè)操作可以依賴其他多個(gè)操作。開發(fā)者能夠制定操作之間的依賴體系,使特定的操作必須在另外一個(gè)操作順利執(zhí)行完畢后方可執(zhí)行。
- 通過鍵值觀測機(jī)制監(jiān)控NSOperation對(duì)象的屬性。NSOperation對(duì)象有許多屬性都適合通過鍵值觀測機(jī)制(簡稱KVO)來監(jiān)聽。比如可以通過isCancelled屬性來判斷任務(wù)是否已取消,又比如可以通過isFinished屬性來判斷任務(wù)是否已完成。
- 指定操作的優(yōu)先級(jí)。操作的優(yōu)先級(jí)表示此操作與隊(duì)列中其他操作之間的優(yōu)先級(jí)關(guān)系。優(yōu)先級(jí)高的操作先執(zhí)行,優(yōu)先級(jí)低的后執(zhí)行。操作隊(duì)列的調(diào)度算法(scheduling algorithm)雖“不透明”(opaque),但必然是經(jīng)過一番深思熟慮才寫成的。反之,GCD則沒有直接實(shí)現(xiàn)此功能的辦法。GCD的隊(duì)列確實(shí)有優(yōu)先級(jí),不過那是針對(duì)整個(gè)隊(duì)列來說的,而不是針對(duì)每個(gè)塊來說的。NSOperation對(duì)象也有“線程優(yōu)先級(jí)”(thread priority),這決定了運(yùn)行此操作的線程處在何種優(yōu)先級(jí)上。用GCD也可實(shí)現(xiàn)此功能,然而采用操作隊(duì)列更簡單,只需設(shè)置一個(gè)屬性。
- 重用NSOperation對(duì)象。系統(tǒng)內(nèi)置了一些NSOperation的子類(比如NSBlockOperation)以供開發(fā)者調(diào)用,要是不想用這些固有子類的話,那就得自己來創(chuàng)建了。這些類就是普通的Objective-C對(duì)象,能夠存放任何信息。對(duì)象在執(zhí)行時(shí)可以充分利用存于其中的信息,而且還可以隨意調(diào)用定義在類中的方法。這就比派發(fā)隊(duì)列中那些簡單的塊要強(qiáng)大許多。這些NSOperation類可以在代碼中多次使用,他們符合軟件開發(fā)中的“不重復(fù)”(Do’t Repeat Yourself,DRY)原則。
有一個(gè)API選用了操作隊(duì)列而非派發(fā)隊(duì)列,這就是NSNotificationCenter,這個(gè)方法接受的參數(shù)是塊,而不是選擇子:
- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
本來這個(gè)方法也可以不使用操作隊(duì)列,而是把處理通知事件所用的塊安排在派發(fā)隊(duì)列里。但實(shí)際上并沒有這樣做,其設(shè)計(jì)者顯然使用了高層的Objective-C API。在這種情況下,兩套方案的運(yùn)行效率沒多大差距。設(shè)計(jì)這個(gè)方法的人可能不想使用派發(fā)隊(duì)列,因?yàn)槟菢幼鰧⒁蕾囉贕CD,而這種依賴沒有必要,前面說過,塊本身和GCD無關(guān)。
經(jīng)常會(huì)有人說:應(yīng)該盡可能選用高層API,只在確有必要時(shí)才求助與底層。筆者也同意這個(gè)說法,但我并不盲從。某些功能確實(shí)可以用高層的Objective-C方法來做,但這并不等于說它就一定比底層實(shí)現(xiàn)方案好。要想確定哪種方案更佳,最好還是測試一下性能。
要點(diǎn)
- 在解決多線程與任務(wù)管理問題時(shí),派發(fā)隊(duì)列并非唯一方案。
- 操作隊(duì)列提供了一套高層的Objective-C API,能實(shí)現(xiàn)純GCD所具備的絕大部分功能,而且還能完成一些更為復(fù)雜的操作,那些操作若改用GCD來實(shí)現(xiàn),則需另外編寫代碼。