NSOperation的进阶使用和简单探讨
本文將會(huì)從多個(gè)方面探討NSOperation類和NSOperationQueue類的相關(guān)內(nèi)容
一、簡(jiǎn)介
NSOperation類是iOS2.0推出的,通過(guò)NSThread實(shí)現(xiàn)的,但是效率一般。
從OS X10.6和iOS4推出GCD時(shí),又重寫(xiě)了NSOperation和NSOperationQueue,NSOperation和NSOperationQueue分別對(duì)應(yīng)GCD的任務(wù)和隊(duì)列,所以NSOPeration和NSOperationQueue是基于GCD更高一層的封裝,而且完全地面向?qū)ο蟆5潜菺CD更簡(jiǎn)單易用、代碼可讀性也更高。NSOperation和NSOperationQueue對(duì)比GCD會(huì)帶來(lái)一點(diǎn)額外的系統(tǒng)開(kāi)銷,但是可以在多個(gè)操作Operation中添加附屬。
二、知識(shí)概括
從NSOperation的思維導(dǎo)圖了解的這個(gè)類相關(guān)的整體的知識(shí)點(diǎn):
NSOperation和NSOperationQueue是基于GCD的更高一層的封裝,分別對(duì)應(yīng)GCD的任務(wù)和隊(duì)列,完全地面向?qū)ο蟆?梢酝ㄟ^(guò)start方法直接啟動(dòng)NSOperation子類對(duì)象,并且默認(rèn)同步執(zhí)行任務(wù),將NSOperation子類對(duì)象添加到NSOperationQueue中,該隊(duì)列默認(rèn)并發(fā)的調(diào)度任務(wù)。
開(kāi)啟操作有二種方式,一是通過(guò)start方法直接啟動(dòng)操作,該操作默認(rèn)同步執(zhí)行,二是將操作添加到NSOperationQueue中,然后由系統(tǒng)從隊(duì)列中獲取操作然后添加到一個(gè)新線程中執(zhí)行,這些操作默認(rèn)并發(fā)執(zhí)行。
具體實(shí)現(xiàn)如下:
方式一:直接由NSOperation子類對(duì)象啟動(dòng)。
首先將需要執(zhí)行的操作封裝到NSOperation子類對(duì)象中,然后該對(duì)象調(diào)用Start方法。
方式二:當(dāng)添加到NSOperationQueue對(duì)象中,由該隊(duì)列對(duì)象啟動(dòng)操作。
使用隊(duì)列來(lái)執(zhí)行操作,分為2個(gè)階段:第一階段:添加到線程隊(duì)列的過(guò)程,是上圖的步驟1和2。第二階段:系統(tǒng)自動(dòng)從隊(duì)列中取出線程,并且自動(dòng)放到線程中執(zhí)行,是上圖的步驟3和4。
接下來(lái)相關(guān)內(nèi)容的總結(jié):
1. NSOperation
NSOperation是一個(gè)和任務(wù)相關(guān)的抽象類,不具備封裝操作的能力,必須使用其子類。
使用NSOperation?類的方式有3種:
- 系統(tǒng)實(shí)現(xiàn)的具體子類:NSInvocationOperation
- 系統(tǒng)實(shí)現(xiàn)的具體子類:NSBlockOperation
- 自定義子類,實(shí)現(xiàn)內(nèi)部相應(yīng)的?法
該類是線程安全的,不必管理線程生命周期和同步等問(wèn)題。
a. NSInvocationOperation子類
NSInvocationOperation是NSOperation的子類。創(chuàng)建操作對(duì)象的方式有2種,使用initWithTarget:selector:object:創(chuàng)建sel參數(shù)是一個(gè)或0個(gè)的操作對(duì)象。使用initWithInvocation:方法,添加sel參數(shù)是0個(gè)或多個(gè)操作對(duì)象。在未添加到隊(duì)列的情況下,創(chuàng)建操作對(duì)象的過(guò)程中不會(huì)開(kāi)辟線程,會(huì)在當(dāng)前線程中執(zhí)行同步操作。創(chuàng)建完成后,直接調(diào)用start方法,會(huì)啟動(dòng)操作對(duì)象來(lái)執(zhí)行操,或者添加到NSOperationQueue隊(duì)列中。無(wú)論使用該子類的哪個(gè)在初始化的方法,都會(huì)在添加一個(gè)任務(wù)。 和NSBlockOperation子類不同的是,因?yàn)闆](méi)有額外添加任務(wù)的方法,使用NSInvocationOperation創(chuàng)建的對(duì)象只會(huì)有一個(gè)任務(wù)。
默認(rèn)情況下,調(diào)用start方法不會(huì)開(kāi)辟一個(gè)新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行任務(wù)。只有將其放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作
b. NSBlockOperation子類
可以通過(guò)blockOperationWithBlock:創(chuàng)建NSBlockOperation對(duì)象,在創(chuàng)建的時(shí)候也添加一個(gè)任務(wù)。如果想添加更多的任務(wù),可以使用addExecutionBlock:方法。也可以通過(guò)init:創(chuàng)建NSBlockOperation對(duì)象。但是這種創(chuàng)建方式并不會(huì)在創(chuàng)建對(duì)象的時(shí)候添加任務(wù),同樣可以使用addExecutionBlock:方法添加任務(wù)。對(duì)于啟動(dòng)操作和NSInvocationOperation類一樣,都可以通過(guò)調(diào)用start方法和添加NSOperationQueue中來(lái)執(zhí)行操作。
關(guān)于任務(wù)的的同步、異步的執(zhí)行可以總結(jié)幾點(diǎn):
注意:不可在completionBlock屬性的block中追加任務(wù),因?yàn)樵诓僮饕呀?jīng)啟動(dòng)執(zhí)行中或者結(jié)束后不可以添加block任務(wù)。
c. 自定義子類
一般類NSInvocationOperation、NSBlockOperation就可以滿足使用需要,當(dāng)然還可以自己自定義子類。
創(chuàng)建的子類時(shí),需要考慮到可能會(huì)添加到串行和并發(fā)隊(duì)列的不同情況,需要重寫(xiě)不同的方法。對(duì)于串行操作,僅僅需要重新main方法就行,在這個(gè)方法中添加想要實(shí)現(xiàn)的功能。對(duì)于并發(fā)操作,重寫(xiě)四個(gè)方法:start、asynchronous、executing、finished。并且需要自己創(chuàng)建自動(dòng)釋放池,因?yàn)楫惒讲僮鳠o(wú)法訪問(wèn)主線程的自動(dòng)釋放池。
注意:在自定義子類時(shí),經(jīng)常通過(guò)cancelled屬性檢查方法是否取消,并且對(duì)取消的做出響應(yīng)。
2. NSOperationQueue
使用將NSOperation對(duì)象添加NSOperationQueue中,來(lái)管理操作對(duì)象是非常方便的。因?yàn)楫?dāng)我們把操作對(duì)象添加到NSOperationQueue對(duì)象后,該NSOperationQueue對(duì)象從線程中拿取操作、以及分配到對(duì)應(yīng)線程的工作都是由系統(tǒng)處理的。
只要是創(chuàng)建了隊(duì)列,在隊(duì)列中的操作,就會(huì)在子線程中執(zhí)行,并且默認(rèn)并發(fā)操作。添加到子隊(duì)列NSOperationQueue實(shí)例中的操作,都是異步執(zhí)行
a.操作對(duì)象添加到NSOperationQueue對(duì)象中
添加的方式有3種。
- addOperation:添加一個(gè)操作
- addOperationWithBlock:,系統(tǒng)自動(dòng)封裝成一個(gè)NSBlockOperation對(duì)象,然后添加到隊(duì)列中
- addOperations:waitUntilFinished:添加多個(gè)操作
操作對(duì)象添加到NSOperationQueue之后,通常短時(shí)間內(nèi)就會(huì)運(yùn)行。但是如果存在依賴,或者整個(gè)隊(duì)列被暫停等原因,也可能需要等待。
操作對(duì)象添加NSOperationQueue中后,不要再修改操作對(duì)象的狀態(tài)。因?yàn)椴僮鲗?duì)象可能會(huì)在任何時(shí)候運(yùn)行,因此改變操作對(duì)象的依賴或數(shù)據(jù)會(huì)產(chǎn)生無(wú)法預(yù)估的問(wèn)題。只能查看操作對(duì)象的狀態(tài), 比如是否正在運(yùn)行、等待運(yùn)行、已經(jīng)完成等。
b. 設(shè)置最多并發(fā)數(shù)
雖然NSOperationQueue類設(shè)計(jì)用于并發(fā)執(zhí)行操作,但是也可以強(qiáng)制讓單個(gè)隊(duì)列一次只能調(diào)度一個(gè)操作對(duì)象。setMaxConcurrentOperationCount:方法可以設(shè)置隊(duì)列的最大并發(fā)操作數(shù)量。當(dāng)設(shè)為1就表示NSOperationQueue實(shí)例每次只能執(zhí)行一個(gè)NSOperation子類對(duì)象。不過(guò)操作對(duì)象執(zhí)行的順序會(huì)依賴于其它因素,比如操作是否準(zhǔn)備好和操作對(duì)象的優(yōu)先級(jí)等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue。
maxConcurrentOperationCount默認(rèn)是-1,不可設(shè)置為0。如果沒(méi)有設(shè)置最大并發(fā)數(shù),那么并發(fā)的個(gè)數(shù)是由系統(tǒng)內(nèi)存和CPU決定的。
相關(guān)概念:
c. 進(jìn)度修改
一個(gè)操作執(zhí)行還未完成時(shí),我們可能需要讓該任務(wù)暫停、可能之后在進(jìn)行某些操作后又希望繼續(xù)執(zhí)行。為了滿足這個(gè)需要,蘋果公司,為我們提供了suspended屬性。當(dāng)可能我們不想執(zhí)行某些操作時(shí),可以個(gè)cancel方法、cancelAllOperations方法可以取消操作對(duì)象,一旦調(diào)用了這2個(gè)方法,操作對(duì)象將無(wú)法恢復(fù)。具體如下:
對(duì)于暫停操作,當(dāng)NSOperationQueue對(duì)象屬性suspended設(shè)置為YES,隊(duì)列會(huì)停止對(duì)任務(wù)調(diào)度。對(duì)那些還在線程中的操作有影響的。如果任務(wù)正在執(zhí)行將不會(huì)受到影響,因?yàn)槿蝿?wù)已經(jīng)被隊(duì)列調(diào)度到一個(gè)線程上并執(zhí)行。
對(duì)于繼續(xù)操作,當(dāng)屬性suspended設(shè)置為NO會(huì)繼續(xù)執(zhí)行線程操作。隊(duì)列將積極啟動(dòng)隊(duì)列中已準(zhǔn)備執(zhí)行的操作。
一旦NSOperation子類操作對(duì)象添加到NSOperationQueue對(duì)象中,該隊(duì)列就擁有了該操作對(duì)象并且不能刪除操作對(duì)象,如果不想執(zhí)行操作對(duì)象,只能取消該操作對(duì)象。關(guān)于取消操作,可以分為2種情況,取消一個(gè)操作和取消一個(gè)隊(duì)列的全部操作二種情況。調(diào)用NSOperation類實(shí)例的cancel方法取消單個(gè)操作對(duì)象。調(diào)用NSOperationQueue類實(shí)例cancelAllOperations方法取消隊(duì)列中全部操作對(duì)象。
對(duì)于隊(duì)列中的操作,只有操作標(biāo)記為已結(jié)束才能被隊(duì)列移除。在隊(duì)列中未被調(diào)度的操作,會(huì)調(diào)用start方法執(zhí)行操作,以便操作對(duì)象處理取消事件。然后標(biāo)記這些操作對(duì)象為已結(jié)束。對(duì)于正在線程中執(zhí)行其任務(wù)的操作對(duì)象,正在執(zhí)行的任務(wù)會(huì)繼續(xù)執(zhí)行,該操作對(duì)象會(huì)被標(biāo)記經(jīng)結(jié)束。
注意:只會(huì)停止調(diào)度隊(duì)列中操作對(duì)象,正在執(zhí)行任務(wù)的依然會(huì)執(zhí)行,且取消不可恢復(fù)。
d.作用
NSOperation對(duì)象可以調(diào)?start?法來(lái)執(zhí)?任務(wù),但默認(rèn)是同步執(zhí)行的(可以創(chuàng)建異步操作,NSBlockOperation添加操作數(shù)大于1時(shí),除第一個(gè)任務(wù)外,其任務(wù)就是異步執(zhí)行)。如果將NSOperation添加到NSOperationQueue中,之后操作就就由系統(tǒng)管理,系統(tǒng)先從隊(duì)列中取出操作,然后放到一個(gè)新線程中異步執(zhí)行。總結(jié):添加操作到NSOperationQueue中,自動(dòng)執(zhí)行操作,自動(dòng)開(kāi)啟線程
f. 獲取隊(duì)列
系統(tǒng)提供了2個(gè),可以獲取當(dāng)前隊(duì)列和主隊(duì)列。可以通過(guò)類屬性currentQueue獲取當(dāng)前隊(duì)列。可以通過(guò)類屬性mainQueue獲取主隊(duì)列.
3.依賴
操作對(duì)象可以添加和移除依賴。當(dāng)一個(gè)操作對(duì)象添加了依賴,被依賴的操作對(duì)象就會(huì)先執(zhí)行,當(dāng)被依賴的操作對(duì)象執(zhí)行完才會(huì)當(dāng)前的操作對(duì)象。添加到不同線程對(duì)象中的操作對(duì)象依然彼此間可以單方面依賴。切記循環(huán)依賴的情況。這樣會(huì)產(chǎn)生死循環(huán)。
可以通過(guò)addDependency方法添加一個(gè)或者多個(gè)依賴的對(duì)象。eg:[A addDependency:B];
操作A依賴于操作B。操作對(duì)象會(huì)管理自己的依賴,因此在不相同隊(duì)列中的操作對(duì)象可以建立依賴關(guān)系。但是一定要在添加線程對(duì)象NSOperationQueue之前,進(jìn)行依賴設(shè)置。設(shè)置依賴可以保證執(zhí)行順序,操作添加到隊(duì)列添加的順序并不能決定執(zhí)行順序,執(zhí)行的順序取決于多種因素比如依賴、優(yōu)先級(jí)等。
調(diào)用removeDependency:方法移除依賴。
如圖,箭頭方向就是依賴的對(duì)象,從圖中可知,A依賴b,而b依賴C。所以三者的執(zhí)行順序是C-->b-->A
4.線程安全
在NSOperation實(shí)例在多線程上執(zhí)行是安全的,不需要添加額外的鎖
5.cancel方法
只會(huì)對(duì)未執(zhí)行的操作有效,正在執(zhí)行的操作,在收到cancel消息后,依然會(huì)執(zhí)行。
調(diào)用操作隊(duì)列中的操作的cancel方法,且該操作隊(duì)列具有未完成的依賴操作時(shí),那么這些依賴操作會(huì)被忽略。由于操作已經(jīng)被取消,因此此行為允許隊(duì)列調(diào)用操作的start方法,以便在不調(diào)用其主方法的情況下從隊(duì)列中刪除操作。如果對(duì)不在隊(duì)列中的操作調(diào)用cancel方法,則該操作立即標(biāo)記為已取消。
6.狀態(tài)屬性
一個(gè)線程有未創(chuàng)建、就緒、運(yùn)行中、阻塞、消亡等多個(gè)狀態(tài)。而操作對(duì)象也有多種狀態(tài):executing(執(zhí)行中)、finished(完成)、ready(就緒)狀態(tài),這三個(gè)屬性是蘋果公司,提供給我們用于觀察操作對(duì)象狀態(tài)的時(shí)候用的。因?yàn)檫@個(gè)三個(gè)屬性KVC與KVO兼容的,因此可以監(jiān)聽(tīng)操作對(duì)象狀態(tài)屬性。
7.操作完成
a. 監(jiān)聽(tīng)操作完成
當(dāng)我們可能需要在某個(gè)操作對(duì)象完成后添加一些功能,此時(shí)就可以用屬性completionBlock來(lái)添加額外的內(nèi)容了。
b. 等待操作完成
這個(gè)有2種情況:一是等待單個(gè)操作對(duì)象,而是等待隊(duì)列里全部的操作。
如果想等待整個(gè)隊(duì)列的操作,可以同時(shí)等待一個(gè)queue中的所有操作。使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。在等待一個(gè)隊(duì)列時(shí),應(yīng)用的其它線程仍然可以往隊(duì)列中添加操作,因此可能會(huì)加長(zhǎng)線程的等待時(shí)間。
// 阻塞當(dāng)前線程,等待queue的所有操作執(zhí)行完畢 [queue waitUntilAllOperationsAreFinished];對(duì)于單個(gè)操作對(duì)象,為了最佳的性能,盡可能設(shè)計(jì)異步操作,這樣可以讓?xiě)?yīng)用在正在執(zhí)行操作時(shí)可以去處理其它事情。如果需要當(dāng)前線程操作對(duì)象處理完成后的結(jié)果,可以使用NSOperation的waitUntilFinished方法阻塞當(dāng)前線程,等待操作完成。通常應(yīng)該避免這樣編寫(xiě),阻塞當(dāng)前線程可能是一種簡(jiǎn)便的解決方案,但是它引入了更多的串行代碼,限制了整個(gè)應(yīng)用的并發(fā)性,同時(shí)也降低了用戶體驗(yàn)。絕對(duì)不要在應(yīng)用主線程中等待一個(gè)Operation,只能在非中等待。因?yàn)樽枞骶€程將導(dǎo)致應(yīng)用無(wú)法響應(yīng)用戶事件,應(yīng)用也將表現(xiàn)為無(wú)響應(yīng)。
// 會(huì)阻塞當(dāng)前線程,等到某個(gè)operation執(zhí)行完畢 [operation waitUntilFinished];8.執(zhí)行順序
添加到NSOperationQueue中的操作對(duì)象,其執(zhí)行順序取決于2點(diǎn):
1.首先判斷操作對(duì)象是否已經(jīng)準(zhǔn)備好:由對(duì)象的依賴關(guān)系確定
2.然后再根據(jù)所有操作對(duì)象的相對(duì)優(yōu)先級(jí)來(lái)確定:優(yōu)先級(jí)等級(jí)則是操作對(duì)象本身的一個(gè)屬性。默認(rèn)所有操作對(duì)象都擁有“普通”優(yōu)先級(jí),不過(guò)可以通過(guò)qualityOfService:方法來(lái)提升或降低操作對(duì)象的優(yōu)先級(jí)。優(yōu)先級(jí)只能應(yīng)用于相同隊(duì)列中的操作對(duì)象。如果應(yīng)用有多個(gè)操作隊(duì)列,每個(gè)隊(duì)列的優(yōu)先級(jí)等級(jí)是互相獨(dú)立的。因此不同隊(duì)列中的低優(yōu)先級(jí)操作仍然可能比高優(yōu)先級(jí)操作更早執(zhí)行。
對(duì)于優(yōu)先級(jí),我們可以使用屬性queuePriority給某個(gè)操作對(duì)象設(shè)置高底,優(yōu)先級(jí)高的任務(wù),調(diào)用的幾率會(huì)更大, 并不能保證執(zhí)行順序。并且優(yōu)先級(jí)不能替代依賴關(guān)系,優(yōu)先級(jí)只是對(duì)已經(jīng)準(zhǔn)備好的操作對(duì)象確定執(zhí)行順序。先滿足依賴關(guān)系,然后再根據(jù)優(yōu)先級(jí)從所有準(zhǔn)備好的操作中選擇優(yōu)先級(jí)最高的那個(gè)執(zhí)行。
9.服務(wù)質(zhì)量
根據(jù)CPU,網(wǎng)絡(luò)和磁盤的分配來(lái)創(chuàng)建一個(gè)操作的系統(tǒng)優(yōu)先級(jí)。一個(gè)高質(zhì)量的服務(wù)就意味著更多的資源得以提供來(lái)更快的完成操作。涉及到CPU調(diào)度的優(yōu)先級(jí)、IO優(yōu)先級(jí)、任務(wù)運(yùn)行所在的線程以及運(yùn)行的順序等等
通過(guò)設(shè)置屬性qualityOfService來(lái)設(shè)置服務(wù)質(zhì)量。QoS 有五種優(yōu)先級(jí),默認(rèn)為NSQualityOfServiceDefault。它的出現(xiàn)統(tǒng)一了Cocoa中所有多線程技術(shù)的優(yōu)先級(jí)。在此之前,NSOperation和NSThread都通過(guò)threadPriority來(lái)指定優(yōu)先級(jí),而 GCD 則是根據(jù) DISPATCH_QUEUE_PRIORITY_DEFAULT 等宏定義的整形數(shù)來(lái)指定優(yōu)先級(jí)。正確的使用新的 QoS 來(lái)指定線程或任務(wù)優(yōu)先級(jí)可以讓 iOS 更加智能的分配硬件資源,以便于提高執(zhí)行效率和控制電量。
三、相關(guān)類介紹
NSOperation
NSOperation是NSObject的子類,表示單個(gè)工作單元。它是一個(gè)與任務(wù)的相關(guān)抽象類,為狀態(tài)、優(yōu)先級(jí)、依賴關(guān)系和管理提供了一個(gè)有用的、線程安全的結(jié)構(gòu)。
創(chuàng)建自定義NSOperation子類是沒(méi)有意義的,Foundation提供了具體的實(shí)現(xiàn)的子類:NSBlockOperation和NSInvocationOperation。
適合于NSOperation的任務(wù)的例子包括網(wǎng)絡(luò)請(qǐng)求、圖像調(diào)整、文本處理或任何其他可重復(fù)的、結(jié)構(gòu)化的、長(zhǎng)時(shí)間運(yùn)行的任務(wù),這些任務(wù)會(huì)產(chǎn)生關(guān)聯(lián)的狀態(tài)或數(shù)據(jù)。
概觀
因?yàn)镹SOperation類是一個(gè)抽象類,并不具備封裝操作的能力,所以不能直接使用該類,而是應(yīng)該使用其子類來(lái)執(zhí)行實(shí)際的任務(wù)。其子類包括2種,系統(tǒng)定義的子類(NSInvocationOperation或NSBlockOperation)和自定義的子類。雖然NSOperation類是抽象類,但是該類的基本實(shí)現(xiàn)中包括了安全執(zhí)行任務(wù)的重要邏輯。這個(gè)內(nèi)置邏輯的存在可以讓你專注于任務(wù)的實(shí)際實(shí)現(xiàn),而不是專注于編寫(xiě)能保證它與其他系統(tǒng)對(duì)象的正常工作的粘合代碼。
一個(gè)操作對(duì)象是一個(gè)單發(fā)對(duì)象,也就是說(shuō),一旦它執(zhí)行了其任務(wù),將不能再執(zhí)行一遍。通常通過(guò)添加他們到一個(gè)操作隊(duì)列(NSOperationQueue類的一個(gè)實(shí)例)中來(lái)執(zhí)行操作。操作隊(duì)列通過(guò)讓操作在輔助線程(非主線程)上運(yùn)行,或間接使用libdispatch庫(kù)(也稱為GCD)直接來(lái)執(zhí)行其操作。
如果不想使用一個(gè)操作隊(duì)列,可以調(diào)用start方法直接來(lái)執(zhí)行一個(gè)操作。手動(dòng)執(zhí)行操作會(huì)增加更多的代碼負(fù)擔(dān),因?yàn)殚_(kāi)啟不在就緒狀態(tài)的操作會(huì)引發(fā)異常。ready屬性表示操作的就緒狀態(tài)。
操作依賴
依賴是一種按照特定順序執(zhí)行操作的便捷方式。可以使用addDependency:、removeDependency:方法給操作添加和刪除依賴。默認(rèn)情況下,直到具有依賴的操作對(duì)象的所有依賴都執(zhí)行完成才會(huì)認(rèn)為操作對(duì)象是ready(就緒)狀態(tài),。一旦最后一個(gè)依賴操作完成,這個(gè)操作對(duì)象會(huì)變成就緒狀態(tài)并且可以執(zhí)行。
NSOperation支持的依賴是不會(huì)區(qū)分其操作是成功的完成還是失敗的完成。(換句話說(shuō),取消操作也視為完成。)由你來(lái)決定有依賴的操作在其所依賴的操作被取消或沒(méi)有成功完成任務(wù)的情況下是否應(yīng)該繼續(xù)。這可能需要合并一些額外的錯(cuò)誤跟蹤功能到操作對(duì)象里。
兼容KVO的屬性
NSOperation類對(duì)其一些屬性是鍵值編碼(KVC)和鍵值觀察(KVO)兼容的。如有需要,可以觀察這些屬性來(lái)控制應(yīng)用程序的其他部分。使用以下鍵路徑來(lái)觀察屬性:
- isCancelled - 只讀
- isAsynchronous - 只讀
- isExecuting - 只讀
- isFinished - 只讀
- isReady - 只讀
- dependencies - 只讀
- queuePriority - 讀寫(xiě)
- completionBlock - 讀寫(xiě)
雖然可以為這些屬性添加觀察者,但是不應(yīng)該使用Cocoa bindings來(lái)把它們和用戶界面相關(guān)的元素綁定。用戶界面相關(guān)的代碼通常只有在應(yīng)用程序的主線程中執(zhí)行。因?yàn)橐粋€(gè)操作可以在任何線程上執(zhí)行,該操作KVO通知同樣可能發(fā)生在任何線程。
如果你為之前的屬性提供了自定義的實(shí)現(xiàn),那么該實(shí)現(xiàn)內(nèi)容必須保持與KVC和KVO的兼容。如果你為NSOperation對(duì)象定義了額外的屬性,建議你同樣需要讓這些屬性保持KVC和KVO兼容。
多核注意事項(xiàng)
可以從多線程中安全地調(diào)用NSOperation對(duì)象的方法而不需要?jiǎng)?chuàng)建額外的鎖來(lái)同步存取對(duì)象。這種行為是必要的,因?yàn)橐粋€(gè)操作的創(chuàng)建和監(jiān)控通常在一個(gè)單獨(dú)的線程上。
當(dāng)子類化NSOperation類時(shí),必須確保任何重寫(xiě)的方法能在多個(gè)線程中是安全的調(diào)用。如果實(shí)現(xiàn)子類中自定義方法,比如自定義數(shù)據(jù)訪問(wèn)器(accessors,getter),必須確保這些方法是線程安全的。因此,訪問(wèn)任何數(shù)據(jù)變量的操作必須同步,以防止?jié)撛诘臄?shù)據(jù)損壞。更多關(guān)于信息同步的,可以查看Threading Programming Guide。
異步操作 VS 同步操作
如果想要手動(dòng)執(zhí)行操作對(duì)象而不是將其添加到一個(gè)隊(duì)列中,那么可以設(shè)計(jì)同步或異步的二種方式來(lái)執(zhí)行操作。操作對(duì)象默認(rèn)是同步的。在同步操作中,操作對(duì)象不會(huì)創(chuàng)建一個(gè)單獨(dú)的線程來(lái)運(yùn)行它的任務(wù)。當(dāng)直接調(diào)用同步操作的start方法時(shí),該操作會(huì)在當(dāng)前線程中立即執(zhí)行。等到這個(gè)對(duì)象的開(kāi)始(start)方法返回給調(diào)用者時(shí),表示該任務(wù)完成。
當(dāng)你調(diào)用一個(gè)異步操作的start方法時(shí),該方法可能在相應(yīng)的任務(wù)完成前返回。一個(gè)異步操作對(duì)象負(fù)責(zé)在一個(gè)單獨(dú)線程上調(diào)度任務(wù)。通過(guò)直接開(kāi)啟新一個(gè)線程、調(diào)用一個(gè)異步方法,或者提交一個(gè)block到調(diào)度隊(duì)列來(lái)執(zhí)行這個(gè)操作。一個(gè)異步操作對(duì)象可以直接啟動(dòng)一個(gè)新線程。(具有開(kāi)辟新線程的能力,但是不一定就好開(kāi)啟新線程,因?yàn)镃PU資源有限,不可能開(kāi)啟無(wú)限個(gè)線程)
如果使用隊(duì)列來(lái)執(zhí)行操作,將他們定義為同步操作是非常簡(jiǎn)單的。如果手動(dòng)執(zhí)行操作,可以將操作對(duì)象定義為異步的。定義一個(gè)異步操作需要更多的工作,因?yàn)槟惚仨毐O(jiān)控正在進(jìn)行的任務(wù)的狀態(tài)和使用報(bào)告KVO通知狀態(tài)的變化。但在你想確保手動(dòng)執(zhí)行操作不會(huì)阻塞調(diào)用線程的情況下定義異步操作是特別有用的。
當(dāng)添加一個(gè)操作到一個(gè)操作隊(duì)列中,隊(duì)列中操作會(huì)忽略了asynchronous屬性的值,總是從一個(gè)單獨(dú)的線程調(diào)用start方法。因此,如果你總是通過(guò)把操作添加到操作隊(duì)列來(lái)運(yùn)行操作,沒(méi)有理由讓他們異步的。
子類化注釋
NSOperation類提供了基本的邏輯來(lái)跟蹤操作的執(zhí)行狀態(tài),但必須從它派生出子類做實(shí)際工作。如何創(chuàng)建子類依賴于該子類設(shè)計(jì)用于并發(fā)還是非并發(fā)。
方法的重載
對(duì)于非并發(fā)操作,通常只覆蓋一個(gè)方法
- main
在該方法中,需要給執(zhí)行特定的任務(wù)添加必要的代碼。當(dāng)然,也可以定義一個(gè)自定義的初始化方法,讓它更容易創(chuàng)建自定義類的實(shí)例。你可能還想定義getter和setter方法來(lái)從操作訪問(wèn)數(shù)據(jù)。然而,如果你定義定制了getter和setter方法,你必須確保這些方法在多個(gè)線程調(diào)用是安全的。
如果你創(chuàng)建一個(gè)并發(fā)操作,需要至少重寫(xiě)下面的方法和屬性:
- start
- asynchronous
- executing
- finished
在并發(fā)操作中,start方法負(fù)責(zé)以異步的方式啟動(dòng)操作。從這個(gè)方法決定否生成一個(gè)線程或者調(diào)用異步函數(shù)。在將要開(kāi)始操作時(shí),start方法也應(yīng)該更新操作executing屬性的執(zhí)行狀態(tài)作為報(bào)告。這可以通過(guò)發(fā)送executing這個(gè)鍵路徑的KVO通知,讓感興趣的客戶端知道該操作現(xiàn)在正在運(yùn)行中。executing屬性還必須以線程安全的方式提供狀態(tài)。
在將要完成或取消任務(wù)時(shí),并發(fā)操作對(duì)象必須生成isExecuting和isFinished鍵路徑的KVO通知為來(lái)標(biāo)記操作的最終改變狀態(tài)。(在取消的情況下,更新isFinished鍵路徑仍然是重要的,即使操作沒(méi)有完全完成其任務(wù)。已經(jīng)排隊(duì)的操作必須在隊(duì)列刪除操作前報(bào)告)除了生成KVO通知,executing和finished屬性的重寫(xiě)還應(yīng)該繼續(xù)根據(jù)操作的狀態(tài)的精確值來(lái)報(bào)告。
重要:
在start方法中,任何時(shí)候都不應(yīng)該調(diào)用super。當(dāng)定義一個(gè)并發(fā)操作時(shí),需要自己提供與默認(rèn)start方法相同的行為,包括啟動(dòng)任務(wù)和生成適當(dāng)?shù)腒VO通知。start方法還應(yīng)該在實(shí)際開(kāi)始任務(wù)之前檢查操作本身是否被取消。
對(duì)于并發(fā)操作,除了上面描述的方法之外,應(yīng)該不需要重寫(xiě)其他方法。然而,如果你自定義操作的依賴特性,可能必須重寫(xiě)額外的方法并提供額外的KVO通知。對(duì)于依賴項(xiàng),這可能只需要提供isReady鍵路徑的通知。因?yàn)閐ependencies屬性包含了一系列依賴操作,所以對(duì)它的更改已經(jīng)由默認(rèn)的NSOperation類處理。
維護(hù)操作對(duì)象狀態(tài)
操作對(duì)象通過(guò)維護(hù)內(nèi)容的狀態(tài)信息來(lái)決定何時(shí)執(zhí)行是安全的和在操作的生命周期期間通知外部其任務(wù)進(jìn)展。自定義子類需要維護(hù)狀態(tài)信息來(lái)保證代碼中執(zhí)行操作的正確性。操作狀態(tài)關(guān)聯(lián)的鍵路徑有:
isReady
該鍵路徑讓客戶端知道一個(gè)操作何時(shí)可以準(zhǔn)備執(zhí)行。當(dāng)操作馬上可以執(zhí)行時(shí)該屬性值為true,當(dāng)其依賴中有未完成,則是false。
大多數(shù)情況下,沒(méi)必要自己管理這個(gè)鍵路徑的狀態(tài)。如果操作的就緒狀態(tài)是由操作依賴因素決定(例如在你的程序中的一些外部條件),那么你可以提供ready屬性的實(shí)現(xiàn)并且跟蹤操作的就緒狀態(tài)。雖然只在外部狀態(tài)允許的情況下創(chuàng)建操作對(duì)象時(shí)通常更簡(jiǎn)單。在macOS 10.6或更高版本中,如果取消的操作,正在等待一個(gè)或多個(gè)依賴操作完成,那么這些依賴項(xiàng)將被忽略,該屬性的值將更新成已經(jīng)準(zhǔn)備好運(yùn)行了。這種行為使操作隊(duì)列有機(jī)會(huì)更快地將已取消的操作從隊(duì)列中清除出去。
isExecuting
該鍵路徑讓客戶端知道操作是否在正在地執(zhí)行它所分配的任務(wù)。如果操作正在處理其任務(wù),則值為true;否則值為false。
如果替換操作對(duì)象的start方法,則還必須替換executing屬性,并在操作的執(zhí)行狀態(tài)發(fā)生變化時(shí)生成KVO通知。
isFinished
該鍵路徑讓客戶端知道操作成功地完成了任務(wù)或者被取消并退出。直到isFinished這個(gè)鍵路徑的值變?yōu)閠rue,操作對(duì)象才會(huì)清除依賴。類似的,直到finished屬性的是true時(shí),一個(gè)操作隊(duì)列才會(huì)退出操作隊(duì)列。因此,將操作標(biāo)記為已完成對(duì)于防止隊(duì)列備份正在進(jìn)行的操作或已取消的操作非常重要。
如果替換操作對(duì)象的start方法,則還必須替換executing屬性,并在操作的執(zhí)行狀態(tài)發(fā)生變化時(shí)生成KVO通知。
isCancelled
isCancelled鍵路徑讓客戶端知道請(qǐng)求取消某個(gè)操作。支持自愿取消,但不鼓勵(lì)主動(dòng)發(fā)送這個(gè)鍵路徑的KVO通知。
響應(yīng)取消命令
一旦將操作添加到隊(duì)列中,操作就不在你的控制范圍內(nèi)了。隊(duì)列接管并處理該任務(wù)的調(diào)度。但是,如果你最終決定不想執(zhí)行某些操作,例如用戶按下取消按鈕或退出應(yīng)用程序時(shí),你可以取消操作,以防止消耗不必要地CPU時(shí)間。可以通過(guò)調(diào)用操作對(duì)象本身的cancel方法或調(diào)用NSOperationQueue類的cancelAllOperations方法來(lái)實(shí)現(xiàn)這一點(diǎn)。
取消一個(gè)操作不會(huì)立即迫使它停止它正在做的事情。雖然所有操作都需要考慮cancelled屬性中的值,但是必須顯式檢查該屬性中的值,并根據(jù)需要中止。NSOperation的默認(rèn)實(shí)現(xiàn)包括取消檢查。例如,如果在調(diào)用一個(gè)操作的start方法之前取消該操作,那么start方法將退出而不啟動(dòng)任務(wù)。
提示
在macOS 10.6或更高版本中,如果調(diào)用操作隊(duì)列中的操作的cancel方法,且該操作隊(duì)列具有未完成的依賴操作,那么這些依賴操作隨后將被忽略。由于操作已經(jīng)被取消,因此此行為允許隊(duì)列調(diào)用操作的start方法,以便在不調(diào)用其主方法的情況下從隊(duì)列中刪除操作。如果對(duì)不在隊(duì)列中的操作調(diào)用cancel方法,則該操作立即標(biāo)記為已取消。在每種情況下,將操作標(biāo)記為已準(zhǔn)備好或已完成時(shí),會(huì)生成適當(dāng)?shù)腒VO通知。
在你編寫(xiě)的任何定制代碼中,都應(yīng)該始終支持取消語(yǔ)義。特別是,主任務(wù)代碼應(yīng)該定期檢查cancelled屬性的值。如果屬性值為YES,則操作對(duì)象應(yīng)該盡快清理并退出。如果您實(shí)現(xiàn)了一個(gè)自定義的start方法,那么該方法應(yīng)該包含早期的取消檢查并適當(dāng)?shù)貓?zhí)行。您的自定義開(kāi)始方法必須準(zhǔn)備好處理這種類型的提前取消。
除了在操作被取消時(shí)簡(jiǎn)單地退出之外,將已取消的操作移動(dòng)到適當(dāng)?shù)淖罱K狀態(tài)也很重要。具體來(lái)說(shuō),如果您自己管理finished和executing屬性的值(可能是因?yàn)槟阏趯?shí)現(xiàn)并發(fā)操作),那么你必須更新更新相應(yīng)地屬性。具體來(lái)說(shuō),你必須將finished返回的值更改為YES,將executing返回的值更改為NO。即使操作在開(kāi)始執(zhí)行之前被取消,你也必須進(jìn)行這些更改。
屬性和方法
初始化
// 返回一個(gè)初始化的NSOperation對(duì)象 - (instancetype)init;// 父類 NSObject方法執(zhí)行操作
// 開(kāi)啟操作 //在當(dāng)前任務(wù)狀態(tài)和依賴關(guān)系合適的情況下,啟動(dòng)NSOperation的main方法任務(wù),需要注意缺省實(shí)現(xiàn)只是在當(dāng)前線程運(yùn)行。如果需要并發(fā)執(zhí)行,子類必須重寫(xiě)這個(gè)方法,并且使屬性asynchronous返回YES。 - (void)start; // 執(zhí)行接收者(NSOperation)的非并發(fā)任務(wù)。操作任務(wù)的入口,一般用于自定義NSOperation的子類 - (void)main; // 操作主任務(wù)完成后執(zhí)行這個(gè)block // 由于NSOperation有可能被取消,所以在block運(yùn)行的代碼應(yīng)該和NSOperation的核心任務(wù)無(wú)關(guān) @property (nullable, copy) void (^completionBlock)(void);取消操作
// 通知操作對(duì)象(NSOperation)停止執(zhí)行其任務(wù)。標(biāo)記isCancelled狀態(tài)。 // 調(diào)用后不會(huì)自動(dòng)馬上取消,需要通過(guò)isCancelled方法檢查是否被取消,然后自己編寫(xiě)代碼退出當(dāng)前的操作 - (void)cancel;獲取操作狀態(tài)
// Boolean 值,表示操作是否已經(jīng)取消 @property (readonly, getter=isCancelled) BOOL cancelled; // Boolean 值,表示操作是否正在執(zhí)行 @property (readonly, getter=isExecuting) BOOL executing; // Boolean 值,表示操作是否正完成執(zhí)行 @property (readonly, getter=isFinished) BOOL finished; // Boolean 值,表示操作是否異步執(zhí)行任務(wù) @property (readonly, getter=isAsynchronous) BOOL asynchronous ; // Boolean 值,表示操作是否可以立即執(zhí)行(準(zhǔn)備完畢狀態(tài)) @property (readonly, getter=isReady) BOOL ready; // 操作的名字 @property (nullable, copy) NSString *name;管理依賴
// 添加依賴,使接收器依賴于指定完成操作。 // 如:[op1 addDependency:op2]; op2先執(zhí)行,op1后執(zhí)行 - (void)addDependency:(NSOperation *)op;// 取消依賴,移出接收方對(duì)指定操作的依賴 // 注意:操作對(duì)象的依賴不能在操作隊(duì)列執(zhí)行時(shí)取消 - (void)removeDependency:(NSOperation *)op;// 在當(dāng)前對(duì)象開(kāi)始執(zhí)行之前必須完成執(zhí)行的操作對(duì)象數(shù)組。 @property (readonly, copy) NSArray<NSOperation *> *dependencies;執(zhí)行優(yōu)先級(jí)
// 操作獲取系統(tǒng)資源的相對(duì)的重要性。系統(tǒng)自動(dòng)合理的管理隊(duì)列的資源分配 @property NSQualityOfService qualityOfService;等待一個(gè)操作對(duì)象
// 阻塞當(dāng)前線程的執(zhí)行,直到操作對(duì)象完成其任務(wù)。可用于線程執(zhí)行順序的同步。 - (void)waitUntilFinished;常量
// 這些常量允許您對(duì)執(zhí)行操作的順序進(jìn)行優(yōu)先排序。 NSOperationQueuePriority // 用于表示工作對(duì)系統(tǒng)的性質(zhì)和重要性。服務(wù)質(zhì)量較高的類比服務(wù)質(zhì)量較低的類獲得更多的資源。 NSQualityOfService // NSOperation優(yōu)先級(jí)的枚舉 typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {NSOperationQueuePriorityVeryLow = -8L,NSOperationQueuePriorityLow = -4L,NSOperationQueuePriorityNormal = 0,NSOperationQueuePriorityHigh = 4,NSOperationQueuePriorityVeryHigh = 8 };在iOS8之后蘋果提供了幾個(gè)Quality of Service枚舉來(lái)使用:user interactive, user initiated, utility 和 background。通過(guò)這些枚舉告訴系統(tǒng)我們?cè)谶M(jìn)行什么樣的工作,然后系統(tǒng)會(huì)通過(guò)合理的資源控制來(lái)最高效的執(zhí)行任務(wù)代碼,其中主要涉及到CPU調(diào)度的優(yōu)先級(jí)、IO優(yōu)先級(jí)、任務(wù)運(yùn)行在哪個(gè)線程以及運(yùn)行的順序等等,我們可以通過(guò)一個(gè)抽象的Quality of Service枚舉參數(shù)來(lái)表明任務(wù)的意圖以及類別
//與用戶交互的任務(wù),這些任務(wù)通常跟UI級(jí)別的刷新相關(guān),比如動(dòng)畫(huà),這些任務(wù)需要在一瞬間完成. NSQualityOfServiceUserInteractive // 由用戶發(fā)起的并且需要立即得到結(jié)果的任務(wù),比如滑動(dòng)scroll view時(shí)去加載數(shù)據(jù)用于后續(xù)cell的顯示,這些任務(wù)通常跟后續(xù)的用戶交互相關(guān),在幾秒或者更短的時(shí)間內(nèi)完成 NSQualityOfServiceUserInitiated // 一些可能需要花點(diǎn)時(shí)間的任務(wù),這些任務(wù)不需要馬上返回結(jié)果,比如下載的任務(wù),這些任務(wù)可能花費(fèi)幾秒或者幾分鐘的時(shí)間 NSQualityOfServiceUtility // 一些可能需要花點(diǎn)時(shí)間的任務(wù),這些任務(wù)不需要馬上返回結(jié)果,比如下載的任務(wù),這些任務(wù)可能花費(fèi)幾秒或者幾分鐘的時(shí)間 NSQualityOfServiceBackground // 一些可能需要花點(diǎn)時(shí)間的任務(wù),這些任務(wù)不需要馬上返回結(jié)果,比如下載的任務(wù),這些任務(wù)可能花費(fèi)幾秒或者幾分鐘的時(shí)間 NSQualityOfServiceDefaulteg:Utility 及以下的優(yōu)先級(jí)會(huì)受到 iOS9 中低電量模式的控制。另外,在沒(méi)有用戶操作時(shí),90% 任務(wù)的優(yōu)先級(jí)都應(yīng)該在 Utility 之下。
NSBlockOperation
NSOperation的子類,管理一個(gè)或多個(gè)塊的并發(fā)執(zhí)行的操作。
概觀
NSBlockOperation類是NSOperation的一個(gè)具體子類,它管理一個(gè)或多個(gè)塊的并發(fā)執(zhí)行。可以使用此對(duì)象一次執(zhí)行多個(gè)塊,而不必為每個(gè)塊創(chuàng)建單獨(dú)的操作對(duì)象。當(dāng)執(zhí)行多個(gè)塊時(shí),只有當(dāng)所有塊都完成執(zhí)行時(shí),才認(rèn)為操作本身已經(jīng)完成。
添加到操作中的塊(block)將以默認(rèn)優(yōu)先級(jí)分配到適當(dāng)?shù)墓ぷ麝?duì)列。
方法屬性
管理操作中的塊
// 創(chuàng)建并返回一個(gè)NSBlockOperation對(duì)象,并添加指定的塊到該對(duì)象中。 + (instancetype)blockOperationWithBlock:(void (^)(void))block; // 將指定的塊添加到要執(zhí)行的塊列表中。 - (void)addExecutionBlock:(void (^)(void))block; // 與接收器關(guān)聯(lián)的塊。 @property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;NSInvocationOperation
NSOperation的子類,管理作為調(diào)用指定的單個(gè)封裝任務(wù)執(zhí)行的操作。
概觀
NSInvocationOperation類是NSOperation的一個(gè)具體子類,可以使用它來(lái)初始化一個(gè)包含在指定對(duì)象上調(diào)用選擇器的操作。這個(gè)類實(shí)現(xiàn)了一個(gè)非并發(fā)操作。
方法屬性
初始化
// 返回一個(gè)用指定的目標(biāo)和選擇器初始化的NSInvocationOperation對(duì)象。 - (instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg; // 返回用指定的調(diào)用對(duì)象初始化的NSInvocationOperation對(duì)象。 - (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;獲取屬性
// 接收者的調(diào)用對(duì)象。 @property (readonly, retain) NSInvocation *invocation; // 調(diào)用或方法的結(jié)果 @property (nullable, readonly, retain) id result;常量
// 如果調(diào)用result方法時(shí)出現(xiàn)錯(cuò)誤,則由NSInvocationOperation引發(fā)的異常名稱。 Result ExceptionsNSOperationQueue
管理操作執(zhí)行的隊(duì)列。
概觀
NSObject子類。操作隊(duì)列根據(jù)其優(yōu)先級(jí)和就緒程度執(zhí)行其排隊(duì)的NSOperation對(duì)象。在添加到操作隊(duì)列后,操作將保持在其隊(duì)列中,直到它報(bào)告其任務(wù)結(jié)束為止。在隊(duì)列被添加后,您不能直接從隊(duì)列中刪除操作。
提示:
操作隊(duì)列保留操作直到完成,隊(duì)列本身保留到所有操作完成。使用未完成的操作掛起操作隊(duì)列可能導(dǎo)致內(nèi)存泄漏。
確定執(zhí)行順序
隊(duì)列中的操作是根據(jù)它們的狀態(tài)、優(yōu)先級(jí)和依賴關(guān)系來(lái)組織的,并相應(yīng)地執(zhí)行。如果所有排隊(duì)的操作都具有相同的queuePriority并準(zhǔn)備好在放入隊(duì)列時(shí)執(zhí)行(也就是說(shuō),它們的就緒屬性返回yes),那么它們將按照提交到隊(duì)列的順序執(zhí)行。否則,操作隊(duì)列總是執(zhí)行優(yōu)先級(jí)最高的操作。
但是不應(yīng)該依賴隊(duì)列語(yǔ)義來(lái)確保操作的特定執(zhí)行順序,因?yàn)椴僮鳒?zhǔn)備狀態(tài)的更改可能會(huì)更改最終的執(zhí)行順序。操作間依賴關(guān)系為操作提供了絕對(duì)的執(zhí)行順序,即使這些操作位于不同的操作隊(duì)列中。一個(gè)操作對(duì)象直到它的所有依賴操作都完成執(zhí)行后才被認(rèn)為準(zhǔn)備好執(zhí)行。
取消操作
結(jié)束任務(wù)并不一定意味著操作完成了任務(wù),一個(gè)操作也可以被取消。取消操作對(duì)象會(huì)將該對(duì)象留在隊(duì)列中,但會(huì)通知該對(duì)象應(yīng)該盡快停止其任務(wù)。對(duì)于當(dāng)前正在執(zhí)行的操作,這意味著操作對(duì)象必須檢查取消狀態(tài),停止它正在執(zhí)行的操作,并將自己標(biāo)記為已結(jié)束。對(duì)于在隊(duì)列排隊(duì)但尚未執(zhí)行的操作,隊(duì)列仍然需要調(diào)用操作對(duì)象的start方法,以便它能夠處理取消事件并將自己標(biāo)記為已結(jié)束。
提示
取消操作會(huì)導(dǎo)致操作忽略它可能具有的依賴項(xiàng)。這種行為使隊(duì)列能夠盡快執(zhí)行操作的start方法。開(kāi)始方法依次將操作移動(dòng)到結(jié)束狀態(tài),以便可以將其從隊(duì)列中刪除。
KVO兼容屬性
NSOperationQueue類是符合鍵值編碼(KVC)和鍵值觀察(KVO)的。可以根據(jù)需要觀察這些屬性,以控制應(yīng)用程序的其他部分。要觀察屬性,使用以下鍵路徑:
- operations - 只讀
- operationCount - 只讀
- maxConcurrentOperationCount - 讀寫(xiě)
- suspended - 讀寫(xiě)
- name - 讀寫(xiě)
雖然可以將觀察者附加到這些屬性,但是不應(yīng)該使用Cocoa bindings(綁定)將它們綁定到用戶界面的相關(guān)的元素。與用戶界面關(guān)聯(lián)的任務(wù)通常只能在應(yīng)用程序的主線程中執(zhí)行。然而與操作隊(duì)列相關(guān)聯(lián)的KVO通知可能發(fā)生在任何線程中。
線程安全
從多個(gè)線程中使用一個(gè)NSOperationQueue對(duì)象是安全的,無(wú)需創(chuàng)建額外的鎖來(lái)同步對(duì)該對(duì)象的訪問(wèn)。
操作隊(duì)列使用調(diào)度框架來(lái)啟動(dòng)其操作的執(zhí)行。因此,操作總是在單獨(dú)的線程上執(zhí)行,而不管它們是被指定為同步的還是異步的。
屬性&方法
訪問(wèn)特定操作隊(duì)列
// 返回與主線程關(guān)聯(lián)的操作隊(duì)列。缺省總是有一個(gè)queue。 @property (class, readonly, strong) NSOperationQueue *mainQueue; // 返回啟動(dòng)當(dāng)前操作的操作隊(duì)列。 @property (class, readonly, strong, nullable) NSOperationQueue *currentQueue;管理隊(duì)列中的操作
// 將指定的操作添加到接收器。 - (void)addOperation:(NSOperation *)op; //將指定的操作添加到隊(duì)列。 - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait; // 在操作中包裝指定的塊并將其添加到接收器。 - (void)addOperationWithBlock:(void (^)(void))block; // 當(dāng)前在隊(duì)列中的操作。 @property (readonly, copy) NSArray<__kindof NSOperation *> *operations; // 隊(duì)列中當(dāng)前的操作數(shù)。 @property (readonly) NSUInteger operationCount; // 取消所有排隊(duì)和執(zhí)行的操作。 - (void)cancelAllOperations; // 阻塞當(dāng)前線程,直到所有接收者的排隊(duì)操作和執(zhí)行操作完成為止 - (void)waitUntilAllOperationsAreFinished;管理操作的執(zhí)行
// 應(yīng)用于使用隊(duì)列執(zhí)行的操作的默認(rèn)服務(wù)級(jí)別。 @property NSQualityOfService qualityOfService; // 可以同時(shí)執(zhí)行的隊(duì)列操作的最大數(shù)量。 @property NSInteger maxConcurrentOperationCount; // 在隊(duì)列中并發(fā)執(zhí)行的默認(rèn)最大操作數(shù)。 NSOperationQueueDefaultMaxConcurrentOperationCount暫停執(zhí)行
// 一個(gè)布爾值,表示隊(duì)列是否在主動(dòng)調(diào)度要執(zhí)行的操作。(suspended 掛起,暫停的) @property (getter=isSuspended) BOOL suspended;當(dāng)該屬性的值為NO時(shí),隊(duì)列將積極啟動(dòng)隊(duì)列中已準(zhǔn)備執(zhí)行的操作。將此屬性設(shè)置為YES時(shí),可以防止隊(duì)列啟動(dòng)任何排隊(duì)著的操作,但是已經(jīng)執(zhí)行的操作將繼續(xù)執(zhí)行。可以繼續(xù)將操作添加到已掛起的隊(duì)列中,但在將此屬性更改為NO之前,這些操作不會(huì)安排執(zhí)行。
操作只有在結(jié)束執(zhí)行后才從隊(duì)列中刪除。但是,為了結(jié)束執(zhí)行,必須首先啟動(dòng)一個(gè)操作。因?yàn)閽炱鸬年?duì)列不會(huì)啟動(dòng)任何新操作,所以它不會(huì)刪除當(dāng)前排隊(duì)但未執(zhí)行的任何操作(包括已取消的操作)。
可以使用鍵值觀察監(jiān)視此屬性值的更改。配置一個(gè)觀察者來(lái)監(jiān)視操作隊(duì)列的suspended鍵路徑。
此屬性的默認(rèn)值是NO。
隊(duì)列配置
// 操作隊(duì)列名稱 @property (nullable, copy) NSString *name; // 用于執(zhí)行操作的調(diào)度隊(duì)列。 @property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue;四、使用
1. NSInvocationOperation
創(chuàng)建:調(diào)用Start方法開(kāi)啟。默認(rèn)情況下,調(diào)用start方法不會(huì)開(kāi)辟一個(gè)新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作。
創(chuàng)建方式一:使用initWithInvocation方法,可以設(shè)置0個(gè)或多個(gè)參數(shù)
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:@selector(addSig:)]; NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sig]; NSString * info = @"NSMethodSignature"; [invo setTarget:self]; [invo setSelector:@selector(addSig:)];//argumentLocation 指定參數(shù),以指針?lè)绞?/ idx 參數(shù)索引,第一個(gè)參數(shù)的起始index是2,因?yàn)閕ndex為1,2的分別是self和selector [invo setArgument:(__bridge void *)(info) atIndex:2]; NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithInvocation:invo]; [invocationOp start];創(chuàng)建方式二:使用initWithTarget
// 初始化 NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpSel:) object:@"111"]; // 操作的第一個(gè) // 執(zhí)行 [invocationOp start];2. NSBlockOperation
創(chuàng)建第一個(gè)操作任務(wù),一般不會(huì)開(kāi)辟新線程,就在當(dāng)前線程中執(zhí)行。之后的任務(wù)都是開(kāi)辟新線程。執(zhí)行異步任務(wù)。
創(chuàng)建方式一:使用init:創(chuàng)建操作對(duì)象,然后使用addExecutionBlock:添加執(zhí)行
NSBlockOperation * op1 = [[NSBlockOperation alloc] init];[op1 addExecutionBlock:^{NSLog(@"1 beign");NSLog(@"1--%@",[NSThread currentThread]);NSLog(@"1 end");}];[op addExecutionBlock:^{NSLog(@"2 beign");NSLog(@"2--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);NSLog(@"2 end");}];[op addExecutionBlock:^{NSLog(@"3 beign");NSLog(@"3--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);NSLog(@"3 end");}];[op1 start];創(chuàng)建方式二:使用blockOperationWithBlock創(chuàng)建操作對(duì)象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"1 beign");NSLog(@"1--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]); // 第一個(gè)操作任務(wù),一般不會(huì)開(kāi)辟新線程。就在當(dāng)前線程中執(zhí)行NSLog(@"1 end");}];// 以下操作任務(wù),會(huì)開(kāi)辟新線程[op addExecutionBlock:^{NSLog(@"2 beign");NSLog(@"2--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);NSLog(@"2 end");}];[op addExecutionBlock:^{NSLog(@"3 beign");NSLog(@"3--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);NSLog(@"3 end");}];[op start];3. NSOperationQueue
3.1. 將操作對(duì)象添加到隊(duì)列中
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"1 beign");NSLog(@"1--%@",[NSThread currentThread]);NSLog(@"1 end"); }]; [queue addOperation:blockOp];3.2. 添加依賴
直接使用start啟動(dòng)一個(gè)操作對(duì)象而非將操作對(duì)象添加到NSOperationQueue對(duì)象中是沒(méi)有意義的。因?yàn)楫?dāng)給操作對(duì)象發(fā)送start消息后,啟動(dòng)操作,如果線程未阻塞會(huì)立即執(zhí)行該任務(wù)。所以就沒(méi)有所謂的執(zhí)行順序。只有將操作對(duì)象添加到NSOperationQueue對(duì)象中,在隊(duì)列調(diào)度的時(shí)候,可以按照依賴、優(yōu)先級(jí)等因素順序的調(diào)度任務(wù)。
注意:一定要在添加線程對(duì)象NSOperationQueue之前,進(jìn)行依賴設(shè)置。否則依賴將無(wú)法達(dá)到預(yù)期效果。
a. 通隊(duì)列之間的依賴
// 創(chuàng)建隊(duì)列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 創(chuàng)建操作NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpSel:) object:@"invocationOp--arg"];NSInvocationOperation *invocationOp2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOp2Sel:) object:@"invocationOp2--arg"];// 設(shè)置依賴,操作invocationOp2的任務(wù)執(zhí)行完,才會(huì)執(zhí)行操作invocationOp的任務(wù)。[invocationOp addDependency:invocationOp2];// 執(zhí)行[queue addOperation:invocationOp];[queue addOperation:invocationOp2];b. 不同隊(duì)列間的依賴
// 創(chuàng)建隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 創(chuàng)建操作 NSBlockOperation *block1Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block1Op -- begin");[NSThread sleepForTimeInterval:3]; // 模擬耗時(shí)操作NSLog(@"block1Op -- end"); }]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block2Op -- begin");[NSThread sleepForTimeInterval:4]; // 模擬耗時(shí)操作NSLog(@"block2Op -- end"); }];// 創(chuàng)建隊(duì)列 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; // 創(chuàng)建操作 NSBlockOperation *block3Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block3Op -- begin");[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作NSLog(@"block3Op -- end"); }]; NSBlockOperation *block4Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block4Op -- begin");[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作NSLog(@"block4Op -- end"); }];// 設(shè)置依賴,操作invocationOp2的任務(wù)執(zhí)行完,才會(huì)執(zhí)行操作invocationOp的任務(wù)。 [block1Op addDependency:block3Op]; [block3Op addDependency:block2Op];// block2Op --> block3Op --> block1Op // 添加操作到隊(duì)列中 [queue addOperation:block1Op]; [queue addOperation:block2Op]; [queue2 addOperation:block3Op]; [queue2 addOperation:block4Op];從上代碼可以得到block1Op、block2Op、block3Op三個(gè)操作的執(zhí)行順序:block2Op --> block3Op --> block1Op。
// 創(chuàng)建操作 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"blockOp");// 模擬耗時(shí)操作[NSThread sleepForTimeInterval:3]; }]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block2Op -- begin");// 等blockOp操作對(duì)象的任務(wù)執(zhí)行完,才能接著往下執(zhí)行[blockOp waitUntilFinished];NSLog(@"block2Op --end"); }]; // 執(zhí)行 [queue addOperation:blockOp]; [queue addOperation:block2Op];3.3. 獲取屬性獲取主隊(duì)列
NSOperationQueue *queue = [NSOperationQueue mainQueue];3.4. 獲取屬性獲取當(dāng)前隊(duì)列
NSOperationQueue *queue = [NSOperationQueue currentQueue];3.5. 進(jìn)度修改:NSOperationQueue隊(duì)列的暫停、繼續(xù)和取消。
// 初始化隊(duì)列 - (NSOperationQueue *)manualQueue{if (!_manualQueue) {_manualQueue = [NSOperationQueue new];_manualQueue.maxConcurrentOperationCount = 2;}return _manualQueue; }NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"1--start");[NSThread sleepForTimeInterval:3];NSLog(@"1--end");}];NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"2--start");[NSThread sleepForTimeInterval:1];NSLog(@"2--end");}];NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"3--start");[NSThread sleepForTimeInterval:4];NSLog(@"3--end");}];NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"4--start");[NSThread sleepForTimeInterval:3];NSLog(@"4--end");}];[self.manualQueue addOperation:blockOperation1];[self.manualQueue addOperation:blockOperation2];[self.manualQueue addOperation:blockOperation3];[self.manualQueue addOperation:blockOperation4];a. 暫停
如果任務(wù)正在執(zhí)行將不會(huì)受到影響。因?yàn)槿蝿?wù)已經(jīng)被隊(duì)列調(diào)度到一個(gè)線程上并執(zhí)行。當(dāng)NSOperationQueue對(duì)象屬性suspended設(shè)置為YES,是隊(duì)列停止了對(duì)任務(wù)調(diào)度。對(duì)那些還在線程中的操作有影響的。
self.manualQueue.suspended = YES;b. 繼續(xù)
隊(duì)列將積極啟動(dòng)隊(duì)列中已準(zhǔn)備執(zhí)行的操作。
self.manualQueue.suspended = NO;c. 取消
對(duì)于隊(duì)列中的操作,只有操作標(biāo)記為已結(jié)束才能被隊(duì)列移除。
3.6. 操作完成
a. 監(jiān)聽(tīng)操作完成
可以在操作執(zhí)行完成后,添加額外的內(nèi)容。使用屬性completionBlock,可以為NSOperation對(duì)象的任務(wù)完成后添加額外的操作。但是不可在completionBlock中追加任務(wù),因?yàn)椴僮?operation)已經(jīng)啟動(dòng)執(zhí)行或者結(jié)束后不可以添加block任務(wù)。
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{// 添加的任務(wù) }]; blockOperation1.completionBlock = ^{// 添加額外的內(nèi)容 }; [blockOperation1 start];b. 監(jiān)聽(tīng)操作完成
當(dāng)執(zhí)行到某個(gè)操作對(duì)象發(fā)送了一個(gè)消息waitUntilFinished:消息。當(dāng)前線程會(huì)被阻塞,之前發(fā)送消息的操作對(duì)象的任務(wù)執(zhí)行完畢。當(dāng)前線程才會(huì)被喚起,進(jìn)入準(zhǔn)備狀態(tài),開(kāi)始執(zhí)行相應(yīng)的任務(wù)。
3.7. 最大并發(fā)量
NSOperationQueue是并發(fā)隊(duì)列,maxConcurrentOperationCount表示最大的并發(fā)數(shù)。
當(dāng)maxConcurrentOperationCount是1時(shí),雖然NSOperationQueue對(duì)象是默認(rèn)并發(fā)的調(diào)度NSOperation對(duì)象,但實(shí)際上,此時(shí),NSOperationQueue對(duì)象是串行隊(duì)列。但是和GCD串行不同的是,依賴和優(yōu)先級(jí)因素會(huì)影響NSOperationQueue對(duì)象調(diào)度任務(wù)的順序。添加NSOperation對(duì)象的順序不一定是調(diào)度的順序。
五、自定義NSOperation子類
---
我們可以定義串行和并發(fā)的2種類型的NSOperation子類。
相關(guān)概念
- 常見(jiàn)使用場(chǎng)景:和網(wǎng)絡(luò)相關(guān),比如圖片下載
- 使用步驟
- 實(shí)現(xiàn)init方法,初始化操作對(duì)象以及一些其他對(duì)象
- 重寫(xiě)main方法,在里面實(shí)現(xiàn)想要執(zhí)行的方法
- 在main方法中,創(chuàng)建自動(dòng)釋放池,因?yàn)槿绻钱惒讲僮?#xff0c;無(wú)法訪問(wèn)主線程的自動(dòng)釋放池
- 經(jīng)常通過(guò)cancelled屬性檢查方法是否取消,并且對(duì)取消的做出響應(yīng)
- 響應(yīng)取消事件
- 取消事件可以在任何時(shí)間發(fā)生
- 定期調(diào)用對(duì)象的isCancelled方法,如果返回“YES”,則立即返回,不再執(zhí)行任務(wù)
isCancelled方法本身非常輕量級(jí),可以頻繁調(diào)用,沒(méi)有任何顯著的性能損失
- 位置調(diào)用
- 在執(zhí)行任何實(shí)際工作之前
- 在循環(huán)的每次迭代期間或者如果每次迭代相對(duì)較長(zhǎng),較頻繁時(shí)至少調(diào)用一次
- 在代碼中相對(duì)容易中止操作的任何點(diǎn)
- 重寫(xiě)方法
- 必需重寫(xiě)四個(gè)方法:start、asynchronous、executing、finished
- start(必需):所有并發(fā)操作必須重寫(xiě)此方法,并需要使用自定義的實(shí)現(xiàn)替換默認(rèn)行為。任何時(shí)候都不能調(diào)用父類的start方法。 即不可使用super。重寫(xiě)的start方法負(fù)責(zé)以異步的方式啟動(dòng)一個(gè)操作,無(wú)論是開(kāi)啟一個(gè)線程還是調(diào)用異步函數(shù),都可以在start方法中進(jìn)行。注意在開(kāi)始操作之前,應(yīng)該在start中更新操作的執(zhí)行狀態(tài),因?yàn)橐oKVO的鍵路徑發(fā)送當(dāng)前操作的執(zhí)行狀態(tài),方便查看操作狀態(tài)。
- main(可選):在這個(gè)方法中,放置執(zhí)行給定任務(wù)所需的代碼。應(yīng)該定義一個(gè)自定義初始化方法,以便更容易創(chuàng)建自定義類的實(shí)例。當(dāng)如果定義了自定義的getter和setter方法,必須確保這些方法可以從多個(gè)線程安全地調(diào)用。雖然可以在start方法中執(zhí)行任務(wù),但使用此方法實(shí)現(xiàn)任務(wù)可以更清晰地分離設(shè)置和任務(wù)代碼,即在start方法中調(diào)用mian方法。注意:要定義獨(dú)立的自動(dòng)釋放池與別的線程區(qū)分開(kāi)。
- isFinished(必需):表示是否已完成。需要實(shí)現(xiàn)KVO通知機(jī)制。
- isAsynchronous(必需):默認(rèn)返回 NO ,表示非并發(fā)執(zhí)行。并發(fā)執(zhí)行需要自定義并且返回 YES。后面會(huì)根據(jù)這個(gè)返回值來(lái)決定是否并發(fā)。
- isExecuting(必需):表示是否執(zhí)行中,需要實(shí)現(xiàn)KVO通知機(jī)制。
注意:自己創(chuàng)建自動(dòng)釋放池,異步操作無(wú)法訪問(wèn)主線程的自動(dòng)釋放池
使用
實(shí)現(xiàn)例子如下:
非并發(fā)的情況下需要重寫(xiě)main方法,并且最好添加一個(gè)init方法用于初始化數(shù)據(jù)。
六、GCD VS NSOperation
GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案,會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核),而NSOperation是基于GCD的面向?qū)ο蟮姆庋b,擁有GCD的特性。GCD是將任務(wù)(block)添加到隊(duì)列(串行/并行/全局/主隊(duì)列),并且以同步/異步的方式執(zhí)行任務(wù)的函數(shù),而NSOperation將操作(一般是異步的任務(wù))添加到隊(duì)列(一般是并發(fā)隊(duì)列),就會(huì)執(zhí)行指定操作的函數(shù)。
相對(duì)于NSThread或者是跨平臺(tái)的pthread而言,GCD和NSOperation都是自動(dòng)管理線程的生命周期,開(kāi)發(fā)者只要專注于具體任務(wù)邏輯,不需要編寫(xiě)任何線程管理相關(guān)的代碼。
GCD提供了一些NSOperation不具備的功能:延遲執(zhí)行、一次性執(zhí)行、調(diào)度組;NSOperation里提供了一些方便的操作:最大并發(fā)數(shù)、 隊(duì)列的暫定/繼續(xù)、取消所有的操作、指定操作之間的依賴關(guān)系(GCD可以用同步實(shí)現(xiàn)功能);
GCD是無(wú)法控制線程的最大并發(fā)數(shù)的,而NSOperation可以設(shè)置最大并發(fā)數(shù),可以靈活的根據(jù)需要限制線程的個(gè)數(shù)。因?yàn)殚_(kāi)辟線程需要消耗必要的資源。
何時(shí)使用GCD:
調(diào)度隊(duì)列(Dispatch queues)、分組(groups)、信號(hào)量(semaphores)、柵欄(barriers)組成了一組基本的并發(fā)原語(yǔ)。對(duì)于一次性執(zhí)行,或者簡(jiǎn)單地加快現(xiàn)有方法的速度,使用輕量級(jí)的GCD分派(dispatch)比使用NSOperation更方便。
何時(shí)使用NSOperation:
在特定隊(duì)列優(yōu)先級(jí)和服務(wù)質(zhì)量(用于表示工作對(duì)系統(tǒng)的性質(zhì)和重要性)下, 可以用一系列依賴來(lái)調(diào)度NSOperation對(duì)象 。與在GCD隊(duì)列上調(diào)度的block不同,NSOperation可以被取消和查詢其操作狀態(tài)。通過(guò)子類化,NSOperation可以關(guān)聯(lián)執(zhí)行結(jié)果,以供之后參考。
注意:NSOperation和GCD不是互斥的。
七、隊(duì)列VS線程VS任務(wù)
從思維導(dǎo)圖了解整個(gè)概況。
1. 隊(duì)列(queue)
隊(duì)列是先進(jìn)先出特征數(shù)據(jù)結(jié)構(gòu)。并且隊(duì)列只是負(fù)責(zé)任務(wù)的調(diào)度,而不負(fù)責(zé)任務(wù)的執(zhí)行。。
按照任務(wù)的調(diào)度方式可以分為串行隊(duì)列和并發(fā)隊(duì)列。特點(diǎn)總結(jié)如下:
- 串行隊(duì)列
- 一個(gè)接一個(gè)的調(diào)度任務(wù)
- 無(wú)論隊(duì)列中所指定的執(zhí)行任務(wù)是同步還是異步,都會(huì)等待前一個(gè)任務(wù)執(zhí)行完成后,再調(diào)度后面的任務(wù)。
- 并發(fā)隊(duì)列
- 可以同時(shí)調(diào)度多個(gè)任務(wù)。
- 如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的,會(huì)等待任務(wù)執(zhí)行完成后,再調(diào)度后續(xù)的任務(wù)。
- 如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的,同時(shí)底層線程池有可用的線程資源,會(huì)再新的線程調(diào)度后續(xù)任務(wù)的執(zhí)行。
我們知道系統(tǒng)提供了2個(gè)隊(duì)列:主隊(duì)列和全局并發(fā)隊(duì)列兩種隊(duì)列。我們還可以自己創(chuàng)建隊(duì)列。
- 主隊(duì)列
- 特點(diǎn)
- 添加到主隊(duì)列中的任務(wù),都會(huì)在主線程中執(zhí)行。
- 專門用來(lái)在主線程上調(diào)度任務(wù)的隊(duì)列。
- 在主線程空閑時(shí)才會(huì)調(diào)度隊(duì)列中的任務(wù)在主線程執(zhí)行。
- 不會(huì)開(kāi)啟線程。
- 串行。
- 獲取
- 會(huì)隨著程序啟動(dòng)一起創(chuàng)建。
- 主隊(duì)列只需要獲取不用創(chuàng)建。
- 主隊(duì)列是負(fù)責(zé)在主線程調(diào)度任務(wù)的。
- 特點(diǎn)
- 全局隊(duì)列
- 本質(zhì)是一個(gè)并發(fā)隊(duì)列,由系統(tǒng)提供,方便編程,可以不用創(chuàng)建就直接使用。
- 全局隊(duì)列是所有應(yīng)用程序共享的
- GCD的一種隊(duì)列
- 全局隊(duì)列沒(méi)有名字,但是并發(fā)隊(duì)列有名字。有名字可以便于查看系統(tǒng)日志
- 自定義隊(duì)列
- 有2種方式:串行、并發(fā)。
- 添加到自定義隊(duì)列中的任務(wù),都會(huì)自動(dòng)放在子線程中執(zhí)行。
2. 線程(thread)
- 開(kāi)辟線程具有一定的資源開(kāi)銷,iOS系統(tǒng)下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)、棧空間(子線程512KB、主線程1MB,也可以使用-setStackSize:設(shè)置,但必須是4K的倍數(shù),而且最小是16K),創(chuàng)建線程大約需要90毫秒
- 對(duì)于單核CPU,同一時(shí)間CPU只能處理1條線程,即只有1條線程在執(zhí)行,多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換),如果CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象。
- 線程是CPU調(diào)度和分派且能獨(dú)立運(yùn)行基本單位。
- 線程執(zhí)行任務(wù),實(shí)際做事的功能單元。
- 異步:開(kāi)辟新線程。
3. 任務(wù)(task)
一定要分清隊(duì)列、線程和任務(wù)這三者的關(guān)系:隊(duì)列調(diào)度任務(wù),將任務(wù)添加對(duì)應(yīng)的線程上,然后任務(wù)是在線程中執(zhí)行。
任務(wù)的執(zhí)行分為同步和異步。
- 同步
- 當(dāng)前任務(wù)未完成,不會(huì)執(zhí)行下個(gè)任務(wù)
- 不具備開(kāi)辟新線程能力
- 異步
- 當(dāng)前任務(wù)未完成,同樣可以執(zhí)行下一個(gè)任務(wù)
- 具備開(kāi)辟新線程能力,但是不一定會(huì)開(kāi)辟線程。開(kāi)辟線程需要CPU等資源,而系統(tǒng)資源有限。不可能開(kāi)辟無(wú)限個(gè)線程。
推薦博客
- NSOperation apple官方
- NSHipster NSOperation
轉(zhuǎn)載于:https://www.cnblogs.com/blogwithstudyofwyn/p/10011029.html
總結(jié)
以上是生活随笔為你收集整理的NSOperation的进阶使用和简单探讨的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: php yii model,Yii模型
- 下一篇: oracle 叠加代码写法,利用st_g