日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

NSOperation的进阶使用和简单探讨

發(fā)布時(shí)間:2023/12/19 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NSOperation的进阶使用和简单探讨 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


本文將會(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)操作。

  • 將需要執(zhí)行的操作封裝到NSOperation子類對(duì)象中
  • 將該對(duì)象添加到NSOperationQueue中
  • 系統(tǒng)將NSOperation子類對(duì)象從NSOperationQueue中取出
  • 將取出的操作放到一個(gè)新線程中執(zhí)行
  • 使用隊(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):

  • 任務(wù)數(shù)為1時(shí),即使用blockOperationWithBlock:方法或者init:與addExecutionBlock:二個(gè)方法結(jié)合的方式創(chuàng)建的唯一一個(gè)任務(wù)時(shí),不會(huì)開(kāi)辟新線程,直接在當(dāng)前線程同步執(zhí)行任務(wù)。
  • 任務(wù)數(shù)大于1時(shí),使用blockOperationWithBlock:方法或者init:與addExecutionBlock:二個(gè)方法結(jié)合的方式創(chuàng)建的一個(gè)任務(wù)A,不會(huì)開(kāi)辟線程,直接在當(dāng)前線程同步執(zhí)行任務(wù)。而NSBlockOperation對(duì)象使用addExecutionBlock:方法添加的其他任務(wù)會(huì)開(kāi)辟新線程,異步執(zhí)行任務(wù)。
  • 將操作放到一個(gè)NSOperationQueue中,會(huì)異步執(zhí)行操作任務(wù)。
  • 注意:不可在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)概念:

  • 并發(fā)數(shù): NSOperationQueue隊(duì)列里同時(shí)能調(diào)度的NSOperation對(duì)象數(shù)。
  • 最大并發(fā)數(shù): 同一時(shí)間最多能調(diào)度的NSOperation對(duì)象數(shù)。
  • 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)容了。

    operation.completionBlock = ^{// 完成操作后,可以追加的內(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í)間 NSQualityOfServiceDefault

    eg: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 Exceptions

    NSOperationQueue

    管理操作執(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ì)列移除。

  • 在隊(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é)束。
  • [self.manualQueue cancelAllOperations];

    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ù)。

    // 創(chuàng)建隊(duì)列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 創(chuàng)建操作NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:3]; // 模擬耗時(shí)操作}];NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block2Op -- begin");[blockOp waitUntilFinished]; // 等blockOp操作對(duì)象的任務(wù)執(zhí)行完,才能接著往下執(zhí)行NSLog(@"block2Op --end");}];// 執(zhí)行[queue addOperation:blockOp];[queue addOperation:block2Op];

    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)度的順序。

    // 創(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"); }]; queue.maxConcurrentOperationCount = 1; // 最大并發(fā)個(gè)數(shù) [block1Op addDependency:block2Op];// 添加依賴 // block2Op.queuePriority = NSOperationQueuePriorityHigh ; [queue addOperation:block1Op]; [queue addOperation:block2Op];

    五、自定義NSOperation子類
    ---

    我們可以定義串行和并發(fā)的2種類型的NSOperation子類。

    相關(guān)概念

  • 串行(非并發(fā))的情況
    • 常見(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)
  • 并發(fā)
    • 重寫(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ù)。

    + (instancetype)downloaderOperationWithURLPath:(NSString *)urlPath completeBlock:(CompleteBlock)completeBlock{WNNoCurrentOPration *op = [[WNNoCurrentOPration alloc] init];op.urlPath = urlPath;op.completeBlock = completeBlock;return op; } // main一般只適合自定義非并發(fā)的,在里面實(shí)現(xiàn)想執(zhí)行的任務(wù) - (void)main{// 是異步的話 就會(huì)導(dǎo)致訪問(wèn)不到當(dāng)前的釋放池@autoreleasepool {NSLog(@"%s",__func__);// 當(dāng)處于取消操作,不執(zhí)行任務(wù)功能if (self.isCancelled) return;// 下載圖片的耗時(shí)操作NSURL *url = [NSURL URLWithString:self.urlPath];NSData *data = [NSData dataWithContentsOfURL:url];NSLog(@"已下載 %@",[NSThread currentThread]);UIImage *image = [UIImage imageWithData:data];// 主線程回調(diào),完成操作后通知調(diào)用方完成回調(diào)dispatch_async(dispatch_get_main_queue(), ^{if (self.completeBlock != nil) {self.completeBlock(image);}});} }

    六、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ù)的。
    • 全局隊(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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。