轉(zhuǎn)載:http://nshipster.cn/nsoperation/
我們都知道,讓程序瞬間加載并且快速響應(yīng)的秘訣在于后臺(tái)異步執(zhí)行任務(wù)穴吹。
現(xiàn)在的Objective-C開發(fā)者一般有兩個(gè)選擇迁沫,分別是Grand Central Dispatch或者NSOperation△⒃郏現(xiàn)在GCD已經(jīng)逐漸發(fā)展成主流了嘴秸,所以我們來談?wù)労笳咚帕保粋€(gè)面向?qū)ο蟮慕鉀Q辦法凑队。
NSOperation表示了一個(gè)獨(dú)立的計(jì)算單元则果。作為一個(gè)抽象類,它給了它的子類一個(gè)十分有用而且線程安全的方式來建立狀態(tài)漩氨、優(yōu)先級(jí)西壮、依賴性和取消等的模型〗芯或者款青,你不是很喜歡再自己繼承NSOperation的話,框架還提供NSBlockOperation霍狰,這是一個(gè)繼承自NSOperation且封裝了block的實(shí)體類抡草。
很多執(zhí)行任務(wù)類型的案例都很好的運(yùn)用了NSOperation饰及,包括網(wǎng)絡(luò)請(qǐng)求,圖像壓縮康震,自然語(yǔ)言處理或者其他很多需要返回處理后數(shù)據(jù)的燎含、可重復(fù)的、結(jié)構(gòu)化的腿短、相對(duì)長(zhǎng)時(shí)間運(yùn)行的任務(wù)屏箍。
但是僅僅把計(jì)算封裝進(jìn)一個(gè)對(duì)象而不做其他處理顯然沒有多大用處,我們還需要NSOperationQueue來大顯身手橘忱。
NSOperationQueue控制著這些并行操作的執(zhí)行赴魁,它扮演者優(yōu)先級(jí)隊(duì)列的角色,讓它管理的高優(yōu)先級(jí)操作(NSOperation -queuePriority)能優(yōu)先于低優(yōu)先級(jí)的操作運(yùn)行的情況下钝诚,使它管理的操作能基本遵循先進(jìn)先出的原則執(zhí)行尚粘。此外嫉称,在你設(shè)置了能并行運(yùn)行的操作的最大值(maxConcurrentOperationCount)之后长已,NSOperationQueue還能并行執(zhí)行操作凰锡。
讓一個(gè)NSOperation操作開始,你可以直接調(diào)用-start祈噪,或者將它添加到NSOperationQueue中,添加之后尚辑,它會(huì)在隊(duì)列排到它以后自動(dòng)執(zhí)行辑鲤。
現(xiàn)在讓我們通過怎樣使用和怎樣通過繼承實(shí)現(xiàn)功能來看看NSOperation稍微復(fù)雜的部分。
狀態(tài)
NSOperation包含了一個(gè)十分優(yōu)雅的狀態(tài)機(jī)來描述每一個(gè)操作的執(zhí)行杠茬。
isReady→isExecuting→isFinished
為了替代不那么清晰的state屬性月褥,狀態(tài)直接由上面那些keypath的KVO通知決定,也就是說瓢喉,當(dāng)一個(gè)操作在準(zhǔn)備好被執(zhí)行的時(shí)候宁赤,它發(fā)送了一個(gè)KVO通知給isReady的keypath,讓這個(gè)keypath對(duì)應(yīng)的屬性isReady在被訪問的時(shí)候返回YES栓票。
每一個(gè)屬性對(duì)于其他的屬性必須是互相獨(dú)立不同的决左,也就是同時(shí)只可能有一個(gè)屬性返回YES,從而才能維護(hù)一個(gè)連續(xù)的狀態(tài):-isReady: 返回YES表示操作已經(jīng)準(zhǔn)備好被執(zhí)行, 如果返回NO則說明還有其他沒有先前的相關(guān)步驟沒有完成走贪。-isExecuting: 返回YES表示操作正在執(zhí)行佛猛,反之則沒在執(zhí)行。-isFinished: 返回YES表示操作執(zhí)行成功或者被取消了继找,NSOperationQueue只有當(dāng)它管理的所有操作的isFinished屬性全標(biāo)為YES以后操作才停止出列,也就是隊(duì)列停止運(yùn)行婴渡,所以正確實(shí)現(xiàn)這個(gè)方法對(duì)于避免死鎖很關(guān)鍵。
取消
早些取消那些沒必要的操作是十分有用的缩搅。取消的原因可能包括用戶的明確操作或者某個(gè)相關(guān)的操作失敗越败。
與之前的執(zhí)行狀態(tài)類似,當(dāng)NSOperation的-cancel狀態(tài)調(diào)用的時(shí)候會(huì)通過KVO通知isCancelled的keypath來修改isCancelled屬性的返回值硼瓣,NSOperation需要盡快地清理一些內(nèi)部細(xì)節(jié),而后到達(dá)一個(gè)合適的最終狀態(tài)堂鲤。特別的,這個(gè)時(shí)候isCancelled和isFinished的值將是YES瘟栖,而isExecuting的值則為NO葵擎。
有一件肯定需要注意的事情就是關(guān)于單詞"cancel"的拼法特性,盡管各類英語(yǔ)的習(xí)慣不盡相同半哟,但是對(duì)于NSOperation來說:-cancel: 方法調(diào)用里只需要一個(gè)L(動(dòng)詞)-isCancelled: 屬性里需要兩個(gè)L(形容詞)
優(yōu)先級(jí)
不可能所有的操作都是一樣重要,通過以下的順序設(shè)置queuePriority屬性可以加快或者推遲操作的執(zhí)行:
NSOperationQueuePriorityVeryHigh
NSOperationQueuePriorityHigh
NSOperationQueuePriorityNormal
NSOperationQueuePriorityLow
NSOperationQueuePriorityVeryLow
此外寓涨,有些操作還可以指定threadPriority的值,它的取值范圍可以從0.0到1.0体捏,1.0代表最高的優(yōu)先級(jí)糯崎。鑒于queuePriority屬性決定了操作執(zhí)行的順序几缭,threadPriority則指定了當(dāng)操作開始執(zhí)行以后的CPU計(jì)算能力的分配沃呢,如果你不知道這是什么,好吧薄霜,你可能根本沒必要知道這是什么。
依賴性
根據(jù)你應(yīng)用的復(fù)雜度不同搪缨,將大任務(wù)再分成一系列子任務(wù)一般都是很有意義的鸵熟,而你能通過NSOperation的依賴性實(shí)現(xiàn)。
比如說流强,對(duì)于服務(wù)器下載并壓縮一張圖片的整個(gè)過程呻待,你可能會(huì)將這個(gè)整個(gè)過程分為兩個(gè)操作(可能你還會(huì)用到這個(gè)網(wǎng)絡(luò)子過程再去下載另一張圖片队腐,然后用壓縮子過程去壓縮磁盤上的圖片)。顯然圖片需要等到下載完成之后才能被調(diào)整尺寸迫淹,所以我們定義網(wǎng)絡(luò)子操作是壓縮子操作的依賴为严,通過代碼來說就是:
[resizingOperationaddDependency:networkingOperation];[operationQueueaddOperation:networkingOperation];[operationQueueaddOperation:resizingOperation];
除非一個(gè)操作的依賴的isFinished返回YES,不然這個(gè)操作不會(huì)開始第股。時(shí)時(shí)牢記將所有的依賴關(guān)系添加到操作隊(duì)列很重要,不然會(huì)像走路遇到一條大溝诲锹,就走不過去了喲涉馅。
此外,確保不要意外地創(chuàng)建依賴循環(huán)控漠,像A依賴B悬钳,B又依賴A,這也會(huì)導(dǎo)致杯具的死鎖碉渡。
completionBlock
有一個(gè)在iOS 4和Snow Leopard新加入的十分有用的功能就是completionBlock屬性母剥。
每當(dāng)一個(gè)NSOperation執(zhí)行完畢,它就會(huì)調(diào)用它的completionBlock屬性一次环疼,這提供了一個(gè)非常好的方式讓你能在視圖控制器(View Controller)里或者模型(Model)里加入自己更多自己的代碼邏輯。比如說淋叶,你可以在一個(gè)網(wǎng)絡(luò)請(qǐng)求操作的completionBlock來處理操作執(zhí)行完以后從服務(wù)器下載下來的數(shù)據(jù)伪阶。
對(duì)于現(xiàn)在Objective-C程序員必須掌握的工具中处嫌,NSOperation依然是最基本的一個(gè)斟湃。盡管GCD對(duì)于內(nèi)嵌異步操作十分理想,NSOperation依舊提供更復(fù)雜凝赛、面向?qū)ο蟮挠?jì)算模型,它對(duì)于涉及到各種類型數(shù)據(jù)友存、需要重復(fù)處理的任務(wù)又是更加理想的陶衅。在你的下一個(gè)項(xiàng)目里使用它吧,讓它及帶給用戶歡樂搀军,你自己也會(huì)很開心的。