嘮嗑
最近開始遷移文章读恃,把以前寫的一些文章都遷移到簡(jiǎn)書,也算開始在簡(jiǎn)書安家了舔亭。
前言
本文是對(duì)([WWDC 2015 Session 226: Advanced NSOperations]) 的一個(gè)小結(jié)蹋艺,在這個(gè)視頻中介紹了用NSOperation來組織App中的功能結(jié)構(gòu),使邏輯更清晰剥悟。如果沒有看過這個(gè)視頻可以看看視頻灵寺,有助于加深對(duì)operation的理解。這篇文章更多的是一些理論知識(shí)区岗,沒什么代碼略板,但是理解一些內(nèi)部原理有助于平時(shí)我們?nèi)タ吹谌綆欤热?SDWebImage AFNetworking YYImage等里面對(duì)operation自定義的運(yùn)用慈缔。
1.NSOperation 和NSOperationQueue簡(jiǎn)介
NSOperationQueue是高級(jí)的dispatch_queue_t叮称,但同時(shí)也提供了一些功能特性供開發(fā)者使用。
1.cancelAllOperations:可以取消隊(duì)列中所有還未執(zhí)行的operation
2.maxConcurrentOperationCount:最大并行并發(fā)數(shù)
我們來看一張圖
如果我們?cè)O(shè)置maxConcurrentOperationCount=2藐鹤,我們可以將其看成一個(gè)并行隊(duì)列瓤檐,同一時(shí)刻允許最大的并發(fā)數(shù)為2。只有上一個(gè)operation做完后娱节,下一個(gè)operation才會(huì)開始挠蛉。maxConcurrentOperationCount的默認(rèn)值為default,意思是系統(tǒng)會(huì)開設(shè)適當(dāng)數(shù)量的子線程肄满。
NSOperation的特點(diǎn)
- 可以cancel未執(zhí)行的operation
- 指定相同或者不同隊(duì)列的依賴關(guān)系
- kvo監(jiān)控NSOperation的對(duì)象屬性
- 指定操作優(yōu)先級(jí)
- 重用operation:operation只能被執(zhí)行一次后生命周期就結(jié)束了谴古,不能被多次執(zhí)行。
NSOperation可以看成dispatch_block_t的封裝稠歉。我們來看看operation 的生命周期(圖1)掰担,當(dāng)我們init一個(gè)operation,它的初始狀態(tài)是Pending怒炸,然后在某一時(shí)刻會(huì)到Ready恩敌,這時(shí)operation從queue中拿出開始執(zhí),進(jìn)入executing狀態(tài)横媚,最后執(zhí)行完畢到finished狀態(tài)。在除finished以外的其它三個(gè)狀態(tài)都可以到cancelled狀態(tài)月趟。
圖1
operation各個(gè)狀態(tài)說明:
Ready:一個(gè)operation只有在Ready狀態(tài)才可以被queue執(zhí)行灯蝴。下圖,在一個(gè)串行的queue中孝宗,即使第一次進(jìn)入Ready狀態(tài)的operation是在第四個(gè)被放進(jìn)去的穷躁,但是還是第一個(gè)執(zhí)行。這樣的好處就是因妇,放入串行隊(duì)列的operation我們不需要在operation定義的時(shí)候就確定它的執(zhí)行順序问潭,而是在后面動(dòng)態(tài)的確定執(zhí)行順序。(控制進(jìn)入Ready狀態(tài)的時(shí)機(jī))
image
用這個(gè)Ready屬性我們可以做什么事兒呢婚被?我們可以確定依賴(dependencies)狡忙。默認(rèn)條件下,如果A依賴于B址芯,當(dāng)A進(jìn)入finish狀態(tài)灾茁,那么B才會(huì)進(jìn)入Ready狀態(tài)窜觉。這個(gè)依賴可以存在于不同的operationQueue。一個(gè)operaion直有它依賴的所有operation處于finish狀態(tài)北专,它才回進(jìn)入ready狀態(tài)禀挫。如果我們用queue來管理operation,operation在加入到queue中的某一個(gè)時(shí)刻拓颓,它會(huì)在它依賴的operaions執(zhí)行完畢(或者我們沒有設(shè)置依賴)语婴,在某一個(gè)時(shí)刻自動(dòng)進(jìn)入Ready狀態(tài)并被執(zhí)行。當(dāng)然我們可以手動(dòng)執(zhí)行operation驶睦,但是官方不推薦砰左,因?yàn)檎{(diào)用一個(gè)未Ready狀態(tài)的operation會(huì)導(dǎo)致異常拋出。
Cancel:可以cancel未執(zhí)行的operation啥繁,該方法會(huì)設(shè)置對(duì)象內(nèi)的標(biāo)志位菜职,表明operation不需要執(zhí)行,如果operation已經(jīng)start旗闽,就不能取消了酬核。operation進(jìn)入cancel狀態(tài)只是一個(gè)Bool值的翻轉(zhuǎn),當(dāng)我們繼承operation适室,我們要自己決定cancel時(shí)候需要做什么業(yè)務(wù)嫡意。比如我們?cè)趫?zhí)行一個(gè)網(wǎng)絡(luò)請(qǐng)求,cancel狀態(tài)代表著cancel 一個(gè)urlSession捣辆。當(dāng)我們請(qǐng)求一個(gè)數(shù)據(jù)庫蔬螟,cancel代表我們停止對(duì)數(shù)據(jù)庫進(jìn)行讀寫操作。所以我們最好KVO這個(gè)屬性汽畴,然后做適當(dāng)?shù)倪壿嬀山怼<尤氲絨ueue的operaion如果不處于finish狀態(tài),可以進(jìn)入cancel狀態(tài)忍些。調(diào)用[operaion cancel]鲁猩。默認(rèn)條件下,operation調(diào)用cancel罢坝,也會(huì)標(biāo)志它進(jìn)入finished狀態(tài)廓握。因?yàn)橹挥羞@樣依賴它的operation才有機(jī)會(huì)被執(zhí)行。如果我們自定義operation嘁酿,我們要考慮它進(jìn)入cancel狀態(tài)或者這個(gè)operation沒有被成功執(zhí)行時(shí)候隙券,根據(jù)業(yè)務(wù)是否應(yīng)該讓它進(jìn)入finished。
KVO:operation 的狀態(tài)是滿足KVO的闹司,可以被監(jiān)聽娱仔,可以被監(jiān)聽的keyPath(isCancelled,isAsynchronous,isExecuting,isFinished,isReady等)。注意:因?yàn)閛peraion可能在任何線程內(nèi)被執(zhí)行开仰,如果我們將UI元素和以上屬性做綁定拟枚,那么KVO過來的行為就可能發(fā)生在任何的線程內(nèi)薪铜。保險(xiǎn)的做法是:如果KVO得知通知后要做UI的事情,都扔到主線程中做恩溅。如果我們?cè)谧远xoperaion時(shí)候重寫以上屬性隔箍,也要確保它們可以被KVO和KVC。
線程安全:系統(tǒng)提供的operaion的子類都是線程安全的脚乡,所有用系統(tǒng)的operation我們不需要加鎖蜒滩,當(dāng)我們自定義operaion,提供一些對(duì)外的數(shù)據(jù)存取的方法自己加鎖奶稠。
同步和異步的Operaions:
如果我們手動(dòng)執(zhí)行一個(gè)operation俯艰,而不是把它放入一個(gè)operationQueue進(jìn)行管理。operation自己本身也有同步和異步之分锌订,(默認(rèn)同步)竹握。同步的operation不會(huì)開子線程,會(huì)在調(diào)用start這個(gè)方法的線程內(nèi)執(zhí)行辆飘。而異步的會(huì)自己開啟子線程啦辐,如果我們用queue來管理operaion,我們希望它是同步的蜈项,因?yàn)閝ueue自己會(huì)開子線程芹关,我們只要保證加入queue的operation可以按次序執(zhí)行就行。
如果我們不想用queue和GCD來管理operation但想要利用子線程執(zhí)行operation紧卒,我們應(yīng)該自定義一個(gè)異步的operation侥衬。我們需要追蹤operation的狀態(tài)而且手動(dòng)觸發(fā)KVO∨芊迹或者用KVC觸發(fā)KVO轴总。其實(shí)我們平時(shí)在用點(diǎn)語法用系統(tǒng)的setter的時(shí)候,它是通過KVC設(shè)置的博个,所以自動(dòng)會(huì)觸發(fā)KVO通知肘习,如果我們重載了setter,為了讓屬性符合KVO條件坡倔,必須手動(dòng)觸發(fā)通知。
作為抽象類脖含,我們可以繼承NSOpration罪塔,實(shí)現(xiàn)自己的operation來代碼業(yè)務(wù)邏輯。
同步的operation條件:
1.重載main方法养葵,這個(gè)方法里面放主業(yè)務(wù)邏輯征堪。也需要查看cancel屬性
2.如果我們重載了getter和setter,必須確保調(diào)用是線程安全的关拒。
異步operation條件:
1.重載start方法佃蚜,和asynchronous庸娱,exciting,finished屬性
start方法中我們必須確保operation異步執(zhí)行谐算,在此方法中我們需要改變executing更新狀態(tài)熟尉,發(fā)送executing KVO通知。當(dāng)結(jié)束operation洲脂。必須更新isExecuting和isFinished狀態(tài)斤儿,并觸發(fā)kvo通知。當(dāng)cancel一個(gè)operation時(shí)候恐锦,我們也要更新isFinished狀態(tài)往果,即使此時(shí)operation還未執(zhí)行。在queue 中的operation必須進(jìn)入cancel狀態(tài)后才可以被從operation中移除一铅,
注意:start里面不能調(diào)用[super start];在自定義異步operation中陕贮,我們完全自定義start,已經(jīng)全部模擬了父類默認(rèn)的start行為(start task && send KVO)潘飘,在start方法里面肮之,我們還要查看isCanceled屬性,確保start task前福也,task是不是已經(jīng)被取消局骤。如果我們自定義了dependency,我們還需要發(fā)送isReady的KVO通知暴凑。