日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

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

生活随笔

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

编程问答

NSOperation的进阶使用和简单探讨

發(fā)布時(shí)間:2023/12/19 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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è)需要,蘋(píng)果公司,為我們提供了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è)屬性是蘋(píng)果公司,提供給我們用于觀察操作對(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ò)和磁盤(pán)的分配來(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之后蘋(píng)果提供了幾個(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是蘋(píng)果公司為多核的并行運(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ò),歡迎將生活随笔推薦給好友。

    丁香六月婷婷开心婷婷网 | 国产精品一区二区三区视频免费 | 丁香激情网 | 91福利视频久久久久 | 97在线观看免费视频 | 91中文字幕一区 | 欧美一区二区三区在线看 | 亚洲视频观看 | 久久久国产在线视频 | 久久草网站| 黄色小说在线观看视频 | 日日摸日日| 中文字幕在线观看一区二区三区 | 精品视频在线播放 | 丁香婷婷综合五月 | 狠狠搞,com| 少妇bbbb | 欧美日韩高清一区二区 国产亚洲免费看 | 国产精品原创视频 | 亚洲精品视频二区 | 国内精品久久久久影院男同志 | 久久男女视频 | 91av视频在线观看免费 | www.色国产| 亚洲精品天天 | 国产精品日韩在线观看 | 黄色av网站在线免费观看 | 狠狠干电影 | 久久深夜福利免费观看 | 成人国产精品一区 | 国产日韩精品一区二区三区 | 黄色特级一级片 | 在线观看国产日韩欧美 | 欧美一区中文字幕 | 国产成人一区二区啪在线观看 | 91最新在线观看 | 麻豆免费视频网站 | 亚洲精品91天天久久人人 | 国产中文自拍 | 国产精品九九视频 | 91视频高清| 午夜av一区二区三区 | 国产麻豆精品一区 | av在线短片 | 国产美女网| 成人性生爱a∨ | 欧美日韩在线网站 | 亚洲精品免费在线视频 | 欧美日韩精品在线观看 | 亚洲 欧美日韩 国产 中文 | 亚洲成人精品在线 | 国产亚洲精品精品精品 | 午夜精品一区二区三区在线 | 国产综合在线视频 | 国产99久久久国产 | 日韩欧美国产精品 | 中文字幕一区二区在线播放 | 久久1电影院 | 日韩一区二区免费播放 | 久久国产精品二国产精品中国洋人 | 91精品国产乱码久久 | 日韩1页| 久久久久久久久久国产精品 | 天堂网中文在线 | 91亚州 | 久久久久久久久久久久国产精品 | av中文字幕在线播放 | 欧洲一区精品 | 97碰碰碰| a视频在线观看免费 | 久久久久五月 | 亚洲全部视频 | 五月激情视频 | 日韩理论片在线观看 | 亚洲国产三级在线观看 | 精品久久1 | 国产精品视频在线看 | 美女黄濒| 91毛片在线| 久久久麻豆精品一区二区 | 国产一级91 | 天天在线免费视频 | 日韩免费三区 | 国产三级视频在线 | 久久桃花网 | 91伊人久久大香线蕉蜜芽人口 | 国产成人精品一区二区三区在线 | 公开超碰在线 | 久久久久久免费毛片精品 | 91 在线视频 | 在线观看深夜福利 | 亚洲综合射 | 久久久久久久久久久久亚洲 | 久久成人欧美 | 色婷婷亚洲精品 | 国产精品99久久久久久人免费 | 99久久精品午夜一区二区小说 | 成人黄色在线视频 | 久久人人爽人人爽人人 | 97视频在线观看播放 | 丰满少妇在线观看网站 | 人人爽人人射 | 狠狠狠的干 | 婷婷午夜 | 日韩精品一区在线观看 | 亚洲性少妇性猛交wwww乱大交 | 特级西西人体444是什么意思 | 91精品在线视频观看 | 免费精品久久久 | www.看片网站| 在线观看视频一区二区三区 | 天天碰天天操视频 | 精品国产视频在线 | 色偷偷88888欧美精品久久 | 激情综合啪 | 狠狠狠色丁香综合久久天下网 | 三级av在线免费观看 | 国产成人精品综合 | 在线观看色网 | 国产日韩中文字幕 | 午夜电影一区 | 亚洲高清在线观看视频 | 欧美日韩视频免费看 | 久久精品xxx | 免费一级片久久 | 中文字幕网站 | 欧美日韩一区二区久久 | 国产小视频在线播放 | 草莓视频在线观看免费观看 | 国产精品久久影院 | 欧美午夜精品久久久久久孕妇 | 精品国产一区二区三区av性色 | 欧美另类高清 | 波多野结衣在线观看视频 | 91x色| 久久毛片高清国产 | 丁香花在线视频观看免费 | 国产精品久久久久久麻豆一区 | 狠狠狠综合 | a久久久久| 免费中文字幕 | 国产视频精品免费播放 | 欧美伦理一区二区三区 | 中文字幕在线一区二区三区 | 国产最新网站 | 国产精品成人一区二区三区吃奶 | 国产精品18久久久久久久久 | 91av社区 | 99国产成+人+综合+亚洲 欧美 | 久久久久久高潮国产精品视 | 精品国产一区二区三区蜜臀 | 亚洲 欧洲 国产 日本 综合 | 久久一级电影 | 波多野结衣理论片 | 国产精品99在线播放 | 99热国产精品 | 国产美女在线精品免费观看 | 天天插综合 | 免费看av片网站 | 日韩精品一区二区三区在线视频 | 日日爽| 国产精品日韩欧美一区二区 | 亚洲国产精品激情在线观看 | 久草精品在线 | 国产精品久久久久久久久久久久午 | 成年人在线免费看视频 | 久久成人国产精品免费软件 | 日韩高清在线一区二区 | 亚洲一级黄色片 | 在线观看91 | 99精品视频免费观看视频 | 999久久国精品免费观看网站 | 国产精品美女免费视频 | 久久综合网色—综合色88 | www.综合网.com| 久久av在线播放 | 国产色拍拍拍拍在线精品 | 中午字幕在线观看 | 9999免费视频 | 日韩激情视频在线观看 | 国产乱码精品一区二区蜜臀 | 国产精品久久久久久久久久直播 | 又色又爽又黄 | 在线日韩一区 | 国内小视频| 日韩电影一区二区三区在线观看 | 久久久免费国产 | 国产精品影音先锋 | 成人欧美一区二区三区黑人麻豆 | 国产精品不卡 | 特黄特色特刺激视频免费播放 | 国产高清在线a视频大全 | 18国产精品白浆在线观看免费 | 国产最新视频在线观看 | 又湿又紧又大又爽a视频国产 | 久久精品电影网 | 特级毛片网站 | 一级性视频| 成人在线观看免费视频 | 中文区中文字幕免费看 | 精品中文字幕视频 | 日韩久久久 | 欧美性脚交 | 91精品天码美女少妇 | 操操操日日日干干干 | 国产黄色理论片 | 99热这里有| 欧美一级片在线观看视频 | 成年人app网址 | 亚洲成人精品在线观看 | 国产精品自在线拍国产 | 超碰97中文 | 日产乱码一二三区别在线 | 亚洲午夜激情网 | 免费在线激情电影 | www久久精品 | 成人在线观看影院 | 久久 精品一区 | 激情久久一区二区三区 | 国产精品video爽爽爽爽 | 免费看黄的 | 毛片网站免费在线观看 | 国产99久久久国产精品免费看 | 狠狠色噜噜狠狠狠合久 | 在线免费视频a | 国产1区2区3区在线 亚洲自拍偷拍色图 | 综合色播| 国产精品入口麻豆www | 午夜久久久久久久久久久 | 免费在线观看污网站 | 日本精品在线看 | 色综合咪咪久久网 | 97国产视频| 久久99精品一区二区三区三区 | 天天色天 | 在线电影a | 日本99热 | 最近中文字幕免费视频 | 97热在线观看 | 国产在线不卡精品 | 一区二区三区免费网站 | 国产在线观看a | 国产在线污 | 成人黄色在线电影 | 久久最新 | 国产精品 国内视频 | 中文字幕91视频 | 久久久www成人免费毛片 | 国产伦理一区二区三区 | 婷婷国产在线观看 | 午夜视频免费在线观看 | 亚洲成人av一区二区 | 国产精品无av码在线观看 | 在线播放 亚洲 | 中文字幕在线观看国产 | 午夜av日韩 | 91精品日韩 | 久久人91精品久久久久久不卡 | 欧美一级性生活视频 | 国产在线观看不卡 | 天天综合天天综合 | 91精品少妇偷拍99 | 婷婷丁香九月 | 天天色中文 | 国产日韩欧美自拍 | 色吊丝av中文字幕 | 狠狠色丁香婷婷 | 国产91九色视频 | 亚洲国产一二三 | 国产精品美女在线 | 亚洲成a人片在线www | 国产精品久久久久久吹潮天美传媒 | 久久国产成人午夜av影院宅 | 免费视频久久久 | 不卡视频一区二区三区 | 中文在线a∨在线 | 91av小视频| 国产美女免费视频 | 亚洲一级理论片 | 在线观看av的网站 | 国产原创91| 一级黄色片网站 | 欧美久久综合 | 中文字幕在线资源 | 国产精品美女久久久久久久久久久 | 精品免费久久 | 成人av电影免费观看 | 国内成人av | 91色国产在线 | 国产小视频你懂的 | 国产精品毛片一区视频播 | 天天干视频在线 | 日韩视频一区二区三区 | av观看免费在线 | 日本不卡一区二区 | 日韩狠狠操 | 国产精品久久久久久久久久久杏吧 | 国产视频一区精品 | 久久精选视频 | 免费观看性生交大片3 | 午夜神马福利 | 日韩综合视频在线观看 | 日日摸日日爽 | 成人久久18免费网站麻豆 | 亚洲精品午夜aaa久久久 | 精品国产伦一区二区三区观看方式 | 久久久天堂 | 日本中文字幕一二区观 | 东方av在| 97超碰总站| 精品影院一区二区久久久 | 91麻豆精品国产91 | 九九爱免费视频在线观看 | 久久久免费 | 久久久精品国产一区二区 | 欧美怡红院视频 | 热久在线 | 人人草人人草 | 亚洲国产美女久久久久 | 国产精品99久久99久久久二8 | 久久综合婷婷综合 | 欧美天天综合网 | 国产手机精品视频 | 国产黄色大片 | 深爱婷婷| 久草精品视频在线播放 | 激情丁香| 亚洲国产精品小视频 | 久久av高清 | www国产亚洲精品久久麻豆 | 国产麻豆精品免费视频 | 久久久久久久影视 | 日本久久久久久久久久 | 日本久久成人中文字幕电影 | 欧美色噜噜 | 在线黄av | 精品国内自产拍在线观看视频 | 国产精品免费在线视频 | 天天操天天谢 | 国产日韩欧美在线免费观看 | 激情综合久久 | 蜜臀av夜夜澡人人爽人人桃色 | 久久精品99北条麻妃 | 成人高清av在线 | 国产精品亚洲成人 | 免费av 在线| 亚洲天堂网视频 | 国产精品一区二区果冻传媒 | 中文字幕在线观看免费 | 一区av在线播放 | 亚洲经典视频 | 久久久综合香蕉尹人综合网 | 日韩中文在线电影 | 高清av在线免费观看 | 国产免费观看久久黄 | 2020天天干夜夜爽 | 日本精品视频在线观看 | 精品一区二区在线播放 | 精品夜夜嗨av一区二区三区 | 精品久久久久久久久久久久久 | 午夜久久久久久久久 | 欧美大码xxxx | 国产在线不卡 | 五月天色综合 | 亚洲片在线观看 | 天天干婷婷 | 91精品国产乱码 | 日韩精品免费一区二区在线观看 | 中国一区二区视频 | 黄色av免费电影 | 国产精品欧美日韩在线观看 | 丁香久久婷婷 | 中文字幕 91 | 国产精品99精品久久免费 | 狠狠躁日日躁狂躁夜夜躁av | 国产乱老熟视频网88av | 99精品黄色 | 亚洲永久精品国产 | 天天色天天操综合网 | www.干| 911免费视频 | 国产黄大片 | 久久精品视频在线看 | 久久精品—区二区三区 | 久久99国产综合精品免费 | 国产视频久久久久 | 成人在线免费av | 欧美另类亚洲 | 天天操天天操 | 国产精品久久久99 | 日韩欧美在线视频一区二区 | 中文在线√天堂 | 91视频国产高清 | 亚洲区精品视频 | 西西大胆啪啪 | 日本三级在线观看中文字 | 国产精品男女啪啪 | 国产黄色免费观看 | 国产免费久久av | 国产黄免费看 | 国产伦精品一区二区三区四区视频 | 干 操 插 | 日韩1页| 91精品国产麻豆国产自产影视 | 色在线最新 | 亚州精品天堂中文字幕 | 丁香六月婷婷激情 | 99国产在线 | 久久精品视频一 | 在线观看网站av | 六月丁香婷婷久久 | 韩国一区二区在线观看 | 特级黄色片免费看 | 91视频在线自拍 | 国产a国产| 亚洲综合一区二区精品导航 | 亚洲精品99久久久久中文字幕 | 99久久99热这里只有精品 | 欧美成人性网 | 在线精品视频免费播放 | 国产精品久久久久久99 | 99精品一区 | www久久九 | 亚洲综合色播 | 国产又黄又爽又猛视频日本 | 成人在线视频你懂的 | 最近2019中文免费高清视频观看www99 | 久久久免费少妇 | avove黑丝| 国产精品成人免费精品自在线观看 | 欧美一区二区在线 | 亚洲综合一区二区精品导航 | 波多野结衣电影一区 | 日免费视频| 91精品视频一区二区三区 | 日韩免费成人av | 黄色精品一区 | 99久久99久国产黄毛片 | 天天天天色综合 | 亚洲一区二区三区在线看 | 91在线产啪 | 精品91久久久久 | 婷婷视频在线播放 | 成年人黄色大全 | 波多野结衣在线观看视频 | 久草在线观看 | 日本精品久久久久 | 日韩在线播放欧美字幕 | 九九九九色 | 久久夜色精品国产欧美一区麻豆 | 四虎免费在线观看视频 | 欧美在线视频一区二区三区 | www黄色 | 91精品蜜桃 | 香蕉91视频 | 国产 中文 日韩 欧美 | 中文字幕韩在线第一页 | 最近中文字幕mv | 国产不卡在线视频 | 精品国产电影一区 | 久草免费在线视频观看 | 又爽又黄又刺激的视频 | 丁香花在线视频观看免费 | 97综合视频| a在线v| 国产成人久久av免费高清密臂 | 免费av片在线 | 人人射人人澡 | 国产精品一区二区久久精品爱微奶 | 精品美女久久久久久免费 | 超碰公开在线 | 国产一区二区在线影院 | 粉嫩一区二区三区粉嫩91 | 婷婷亚洲最大 | 免费在线观看黄色网 | 亚洲精品美女久久17c | 欧美久草视频 | 91污污 | 国内免费久久久久久久久久久 | 香蕉精品视频在线观看 | 免费三级av| 国产免费观看久久黄 | 超碰在线94 | 91麻豆精品国产91久久久无需广告 | 99热最新网址 | 午夜视频一区二区三区 | 国产精品乱码久久 | 久久永久免费 | 91麻豆网站| 9999在线视频 | 主播av在线 | 成人影视免费 | 伊人婷婷激情 | 午夜视频一区二区 | 欧美一二三视频 | 久草视频在线新免费 | 99精品视频在线观看播放 | 爱爱一区 | 国产a视频免费观看 | 丝袜+亚洲+另类+欧美+变态 | 久草国产在线观看 | 国产精品刺激对白麻豆99 | 久久免费激情视频 | 精品视频 | 欧美成人精品三级在线观看播放 | 99热超碰在线 | 日本动漫做毛片一区二区 | 在线免费高清视频 | 国产手机视频在线 | 久久久久国产精品一区二区 | 97碰碰视频 | 久久精品精品电影网 | 国产原创在线观看 | 日韩三级视频 | 天天操天天摸天天爽 | 久久久久久高潮国产精品视 | 婷婷网在线 | 国产精品一区二区久久国产 | 日韩女同一区二区三区在线观看 | 久久久国产精品一区二区三区 | 超碰免费久久 | 91精品日韩| 久久超碰网 | 亚洲天堂网站 | 黄色大全视频 | 久久久精品 一区二区三区 国产99视频在线观看 | 日操操| 97精品超碰一区二区三区 | 九九久久影视 | 日韩系列| 日韩精品免费在线 | 欧美网址在线观看 | 欧美日韩在线观看一区二区 | 日韩精品一区电影 | 日韩性xxxx | 成人av中文字幕在线观看 | av黄在线播放 | 蜜臀久久99静品久久久久久 | 日日夜夜人人天天 | 欧美成人黄 | 国产成人免费av电影 | 国产精品一区一区三区 | 国产亚洲一区 | 国产精品成人自产拍在线观看 | 色综合久久久久久中文网 | 中文久久精品 | 91豆花在线观看 | 亚洲精品久久久蜜臀下载官网 | 国产一级免费在线观看 | 久久99精品久久久久婷婷 | 精品免费视频123区 午夜久久成人 | 免费黄色在线网址 | 成人免费毛片aaaaaa片 | 国产精品一二三 | 日韩久久久久久久久 | 国产精品一区二区久久精品爱微奶 | 国产免费观看av | 亚洲精品乱码久久久久久9色 | 四虎8848免费高清在线观看 | 日日草天天草 | 91在线中文 | 国产精品久久久久久久电影 | 99久久99久国产黄毛片 | 字幕网在线观看 | 亚洲国产高清在线观看视频 | 免费网址在线播放 | 91系列在线观看 | 久久国产精品99久久人人澡 | 在线成人国产 | 在线免费高清视频 | 亚洲国产成人高清精品 | 欧美做受xxx | 日韩色一区二区三区 | av无限看| 日日夜夜狠狠干 | 欧美日韩不卡一区二区三区 | 激情五月婷婷丁香 | 少妇按摩av| 热九九精品 | 免费视频99 | 日韩激情视频在线观看 | 97高清视频| 亚洲一级免费电影 | 精品国产一区二区在线 | 天天射天天干天天插 | 国产精品毛片一区视频播 | 日韩视频在线不卡 | 99久久这里有精品 | 中文字幕在线网址 | 天天操天天干天天综合网 | 欧美午夜视频在线 | 精品国产一区二区久久 | 亚洲成人av影片 | 日韩三区在线观看 | 国产精品久久久久久久7电影 | 亚洲精品国产精品乱码不99热 | 欧美午夜a| 欧美va日韩va | 久久人操| 久久精品国产美女 | h久久| 国产精品mv在线观看 | 亚洲精品在线一区二区三区 | 在线观看免费高清视频大全追剧 | 蜜桃视频在线视频 | 91精品日韩 | 波多野结衣在线视频免费观看 | 免费在线黄色av | 国产一级淫片在线观看 | 亚洲综合视频在线观看 | 亚洲成人精品国产 | 91av在线视频播放 | 精品一区二区在线免费观看 | 麻豆影视在线播放 | 精品国产综合区久久久久久 | 91久久国产露脸精品国产闺蜜 | 99日韩精品 | 亚洲欧洲一级 | 久久xx视频 | 久草免费看| 精品久久久久久久久久久久久久久久久久 | 免费网站在线观看成人 | 最近中文字幕大全 | 99久免费精品视频在线观看 | 91精品国产欧美一区二区 | 91人人澡人人爽 | 探花视频在线观看免费版 | 人人射 | 日韩中文在线字幕 | 色婷婷免费 | 97精品国自产拍在线观看 | 懂色av一区二区在线播放 | 国产一区二区手机在线观看 | 免费三级av | 日韩二区在线 | 婷婷伊人五月 | 日韩精品一区二区不卡 | 国产精品一区二区久久精品 | 欧洲精品一区二区 | 欧美最猛性xxx | 91av视频在线免费观看 | 日韩理论电影在线观看 | 久久久久北条麻妃免费看 | 日韩综合色| 婷婷丁香国产 | 久久色亚洲 | 日本护士三级少妇三级999 | 999久久久国产精品 高清av免费观看 | 日韩一二三区不卡 | 特黄一级毛片 | 色爱区综合激月婷婷 | 欧美一级小视频 | 欧美成年人在线观看 | 欧美在线aa | 美女黄频 | 亚洲综合在线观看视频 | 欧美色综合久久 | 99精彩视频| 日日干av| 欧美另类高清 videos | 777奇米四色| 97超碰人人模人人人爽人人爱 | 夜夜澡人模人人添人人看 | 成人一级免费视频 | 日本视频高清 | 婷婷福利影院 | 国产小视频网站 | 精品国产网址 | 亚洲国产中文字幕在线观看 | 国产九九九精品视频 | 激情网站网址 | 日韩在线视频不卡 | 美女网站在线观看 | 91桃色在线免费观看 | 婷婷久久婷婷 | 波多野结衣最新 | 99久久久国产免费 | 国产色a在线观看 | 一级片黄色片网站 | 久久尤物电影视频在线观看 | 偷拍区另类综合在线 | 国产97av| 人成午夜视频 | 国产一区二区三区久久久 | 一区二区三区中文字幕在线 | 久久久免费精品 | 日本精品在线 | 午夜黄色一级片 | 欧美在线资源 | jizz18欧美18| 999久久a精品合区久久久 | 91精品视频在线看 | 色网站免费在线观看 | 精品国产乱码久久久久久1区二区 | 天天碰天天操 | 国产精品毛片完整版 | 手机在线看永久av片免费 | 伊人电影在线观看 | 国产精品一区二区久久国产 | 91成年人在线观看 | 韩国一区二区av | 丝袜美腿亚洲 | 成人在线电影观看 | 国产午夜精品一区二区三区 | 亚洲 av网站| 在线观看亚洲国产精品 | 91传媒在线播放 | 亚洲欧美婷婷六月色综合 | av一级一片 | 狠狠色噜噜狠狠 | 在线观看视频中文字幕 | 天天做天天爱天天爽综合网 | 手机在线看永久av片免费 | 午夜18视频在线观看 | 久久精品系列 | 99精品热 | 色香com. | 国内精品在线看 | 日韩av影视在线 | av一区二区三区在线 | 亚洲禁18久人片 | 操高跟美女 | 丰满少妇对白在线偷拍 | 激情综合婷婷 | 国产精品久久久一区二区 | 天天色天天射综合网 | www视频在线免费观看 | 亚洲三级在线播放 | 欧美日本不卡 | 中文字幕av日韩 | 色婷婷综合久久久久 | 免费视频一区 | 99色国产| 国产精品毛片久久久 | 亚洲最新在线 | 成人免费观看大片 | 在线免费av网| 欧美日韩在线免费观看视频 | 婷婷 中文字幕 | 中文字幕丰满人伦在线 | 亚洲精品视频在线观看免费视频 | 99久久久久成人国产免费 | 日韩av一区二区三区在线观看 | 色婷婷六月天 | 亚洲欧美日韩一二三区 | 久久99精品久久久久久久久久久久 | 国产高清精品在线 | 国产在线国偷精品产拍 | 九九九九九九精品任你躁 | 久久深夜福利免费观看 | 欧美在线视频精品 | 亚洲在线看 | 成人免费大片黄在线播放 | 干天天 | 精品成人久久 | 国产一区精品在线观看 | 久久亚洲国产精品 | 亚洲每日更新 | 日本高清久久久 | 中文字幕中文字幕在线一区 | 久久成人视屏 | 亚洲精品成人网 | 亚洲国产精品传媒在线观看 | 一区二区三区中文字幕在线观看 | 毛片99| 国产精品久久久久久久久久东京 | 六月丁香在线视频 | 黄色一级在线免费观看 | 激情综合狠狠 | 精品视频免费看 | 国产精品99久久久精品免费观看 | 少妇高潮流白浆在线观看 | 欧美亚洲免费在线一区 | 最近能播放的中文字幕 | 国产成人三级三级三级97 | 婷婷六月天天 | 日韩午夜精品福利 | 久久毛片高清国产 | www视频免费在线观看 | 欧美日韩一二三四区 | 中文字幕av一区二区三区四区 | 日黄网站| 美女免费视频网站 | 91在线看视频免费 | 日韩在线视频免费播放 | 狠狠狠干 | 日本成人黄色片 | 中文字幕精品在线 | 在线欧美最极品的av | 欧美一进一出抽搐大尺度视频 | 91桃色在线免费观看 | 黄网站色视频免费观看 | 99国产精品视频免费观看一公开 | 久久视频国产精品免费视频在线 | 日韩极品在线 | 国内精品久久久久久久久久清纯 | 欧美日韩aaaa | 天天婷婷| 亚洲精品国产精品乱码不99热 | 丁香视频五月 | av免费网| 午夜性色 | 人人爽人人爽人人 | 日本黄色一级电影 | 久草在线视频看看 | 久草爱视频 | 久久五月婷婷丁香社区 | 永久免费在线 | 五月宗合网 | 国产亚洲人 | 日韩电影在线视频 | 色视频国产直接看 | 国产91学生粉嫩喷水 | 国产亚洲情侣一区二区无 | 国产高清免费视频 | 中文字幕成人在线 | 欧美日韩一区二区在线 | 久久久久久久免费观看 | 免费影视大全推荐 | 一区二区伦理 | 亚洲第五色综合网 | 国产一级免费电影 | 欧美久久久久久久 | 久久午夜精品视频 | 国产精品成人品 | 国产精品一区欧美 | 青草视频在线 | 亚洲国产视频a | 狠狠操夜夜 | 日韩中午字幕 | 黄色福利网站 | 久久免费在线视频 | 伊人狠狠操 | 天天天综合网 | 91在线播放视频 | 日韩视频一区二区在线 | 精品麻豆| 国产精品福利小视频 | 美州a亚洲一视本频v色道 | 丰满少妇在线观看 | 黄色影院在线播放 | 超碰在线人人爱 | 人交video另类hd | 欧美国产日韩在线观看 | 欧美一级专区免费大片 | 九草在线视频 | 在线观看av不卡 | 五月婷婷六月丁香在线观看 | 日韩美一区二区三区 | 中文字幕高清在线 | 九九热视频在线免费观看 | 久色婷婷 | 在线亚洲激情 | 国产中文视 | 色av男人的天堂免费在线 | www.久久91| 天天操夜夜操 | 久久99久久99精品免费看小说 | av免费网站在线观看 | 51久久成人国产精品麻豆 | 久久在线免费观看 | 久久久久久久久久久久亚洲 | av不卡网站 | 久久影院亚洲 | 97av.com| 日本在线观看视频一区 | 六月丁香激情综合 | 久久国产精品视频免费看 | 国产一级淫片免费看 | 99久e精品热线免费 99国产精品久久久久久久久久 | 国产亚洲精品成人av久久影院 | 中文字幕网站视频在线 | 午夜美女网站 | 最新日韩视频 | 亚洲精品www. | 亚洲色图 校园春色 | 国产蜜臀av | 五月开心综合 | 久久av网址 | 久久免费视频国产 | 欧美高清视频不卡网 | 不卡电影免费在线播放一区 | 亚洲黄网站 | 久久久久久久免费观看 | 国产小视频福利在线 | 网站在线观看你们懂的 | 久久精品99国产国产精 | 欧美一级激情 | 四虎在线免费观看 | 国产精品久久久网站 | 天天艹天天干天天 | 欧美精品久久人人躁人人爽 | 国产精品第52页 | 日韩av午夜 | 国产精品69av | 欧美亚洲xxx | 国产亚洲小视频 | 日韩午夜视频在线观看 | 麻豆国产视频下载 | av成年人电影 | 午夜精品一区二区国产 | www.久久免费视频 | 亚洲国产精品成人综合 | 婷婷婷国产在线视频 | 麻豆国产视频下载 | 日韩成人免费在线观看 | 久久手机看片 | 51精品国自产在线 | 日本激情视频中文字幕 | 日韩欧美视频一区二区三区 | 日日碰狠狠添天天爽超碰97久久 | 婷婷色站 | 91九色porny蝌蚪视频 | av大全在线 | 五月婷婷狠狠 | 91香蕉嫩草| 中文在线最新版天堂 | 日韩在线观看精品 | 精品国产乱码久久久久久浪潮 | 中文字幕在线视频精品 | 黄色av免费电影 | 91麻豆精品国产91久久久使用方法 | 亚洲国产中文字幕在线视频综合 | 日韩三级久久 | 日韩在线观看视频在线 | 韩日在线一区 | 天天天插 | 亚洲第二色 | 91精品爽啪蜜夜国产在线播放 | 色婷婷视频 | 亚洲久久视频 | 婷婷综合电影 | 国产一级片在线播放 | 欧美坐爱视频 | 亚洲精品在线一区二区三区 | 日韩一级片网址 | 中文在线资源 | 深爱激情婷婷网 | 日韩中文字幕视频在线观看 | 亚洲黄色在线免费观看 | 欧美色综合久久 | 日日爱视频 | 天天天天天天天操 | 久久国产精品视频 | 在线观看岛国av | 韩国av免费看| 黄色www免费 | 福利久久 | 狠狠狠色| 午夜av在线 | 亚洲精欧美一区二区精品 | 夜夜摸夜夜爽 | 91热视频 | 在线免费观看亚洲视频 | 在线观看av网 | 亚洲国产一区av | 免费男女羞羞的视频网站中文字幕 | 精品在线观看免费 | 天堂va欧美va亚洲va老司机 | 99爱在线观看 | 国产精品免费观看久久 | 婷婷激情五月 | 国产精品18久久久久久不卡孕妇 | 97狠狠操 | 91成人国产 | 国产精品亚洲片夜色在线 | 日韩精品视频在线观看网址 | 久久综合导航 | 韩国av免费在线 | 国产精品日韩久久久久 | 成人影片免费 | 亚洲美女精品区人人人人 | 国产视频精品在线 | 亚洲天堂网视频在线观看 | av一本久道久久波多野结衣 | www一起操 | 91最新国产 | 美女网站免费福利视频 | 天天干天天操天天 | 狠狠躁18三区二区一区ai明星 | 国产精品二区三区 | 精品人妖videos欧美人妖 | 丁香花在线观看视频在线 | 一区二区欧美日韩 | 欧美久久久久久久久久久久久 | 久久深爱网 | 欧美日韩xxx | 日韩大片在线免费观看 | 久久久人人人 | 欧美淫aaa免费观看 日韩激情免费视频 | 天堂av在线网址 | 日本三级香港三级人妇99 | 午夜视频在线瓜伦 | 狠狠色狠狠色 | 中文字幕在线日亚洲9 | 中文字幕丰满人伦在线 | www,黄视频 | 伊人五月天综合 |