码迷,mamicode.com
首页 > 移动开发 > 详细

iOS 並行編程初步

时间:2017-07-27 09:42:24      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:取消   oss   obj   tty   finish   reference   操作   基本   售票   

  • 原文链接 : iOS Concurrency: Getting Started with NSOperation and Dispatch Queues
  • 原文作者 : Ghareeb Hossam
  • 译文出自 : APPCODA
  • 译者 : kmyhy

  • 並行編程永遠是 iOS 開發中的重要內容。同時也是開發者們必須極力避免的“深水區”。假设你對它沒有一個深刻的理解,那它對於你來說確實非常危險。

    未知的東西總是被認為是危險的。想像一下人們在生活中碰到的各種危險,有多少是真正的危險?一旦人們真正了解了這些危險。這些所謂的危險其實不值一提。

    並行編程是一柄雙刃劍,你必須學會怎样正確地使用和掌握它。它能讓你編寫出高效、高速和響應式的 App,但同時。假设使用不當。它會給你的 App 帶來一場災難。

    所以,在我們开始編寫不论什么並行編程代碼之前,首先來思考一下:你為什麼须要並行編程?以及你應該使用哪個 API 來解決問題?在 iOS 中。我們能够使用不同的 API。本教程將介紹当中兩個最经常使用的 API ── NSOperation 和 Dispatch Queue。

技术分享

為什麼须要並行編程?

假設你擁有豐富的 iOS 編程經驗。

但无论你要創建的是何種類型的 App,你都應該知道並行編程能讓你的 App 跑得更快和更加具有響應式風格。這裡。我來介紹幾個關於學習和使用並行編程的好處:

  • 充分利用 iOS 設備的硬件性能: 現在全部的 iOS 設備都具有多個內核,這就允許開發者以並行的方式同時執行多個任務。你應當充分利用這一特點來享受硬件所帶來的便利。
  • 用戶體驗更佳: 你可能曾經寫過調用網絡服務的代碼,以處理諸如 IO 請求、大規模計算的任務。

    那麼你應該知道。在執行這些操作時,UI 線程會被“凍住”,App 將停止響應。每當用戶遇到這種情況。他們第一反應就是毫不猶豫地關閉你的 App 進程。

    而通過並行編程,這些任務能够放到後臺執行而不會堵塞主線程或者影響用戶操作。在處理繁重的數據加載操作的同時,他們仍然能夠點擊按鈕,滾動視圖或者在 App 中切換窗体。

  • 並行編程中的某些 API。比方 NSOperation 和 dispatch queue 使用起來非常簡單: 創建、管理線程不是一件輕鬆的活計。這就是為什麼大部份開發者一聽到並行編程和多線程代碼就發怵的原因。 可是 iOS 擁有強大、簡單的並行 API。它們會令你徹底放鬆。

    你根本無需和不论什么線程的創建或低級 API 打交道。

    並行 API 會自動為你完毕這些工作。使用這些 API 的另外一個好處,是它們能讓你輕易實現同步以避免競爭條件。

    當多個線程同時访問一個共享資源時。往往會導致競爭條件出現,同時得到錯誤的結果。通過同步機制。我們能够保護好線程之間的共享資源。

關於並行編程,你须要知道什麼?

在本教程中。我們將向你詳細介紹關於並行編程的全部必要知識。同時解除你對它的全部疑慮。

首先我們建議你先看一下關於塊(Swift 閉包)的內容。因為塊在並行 API 中使用得非常普遍。然後再來看我們對 dispatch queue 和 NSOperationQueue 的介紹。我們將逐一介紹這兩個 API 的概念,它們的區別,以及怎样使用它們。

第一部份: GCD (Grand Central Dispatch)

GCD 是最接近於操作系統 Unix 底層的,最常見的處理並行代碼和執行異步操作的 API。GCD 負責創建和管理任務隊列。

首先讓我們來了解什麼是隊列。

隊列是什麼?

隊列是這樣一種數據結構。它以先進先出(FIFO)的方式來管理所存儲的對象。隊列就像是人們在電影院售票窗体進行排隊。票總是先賣給先到的人。

位於隊列前面的人要比位於後面的其他人先買到電影票。在計算機學中的隊列與此類似。第一個加入到隊列中的對象,也是第一個從隊列中移除的對象。

技术分享

圖片來源: FreeImages.com/Sigurd Decroos

Dispatch 隊列

Dispatch 隊列 是一種在 App 中執行異步任務和並行任務的方法。它是这样一種隊列,App 將任務以塊(代碼塊)的方式提交給它。有兩種不同的 Dispatch 隊列:(1)串行隊列,(2)並行隊列。在介紹二者的區別前。你须要首先理解一點,分配給這兩種隊列的任務,其實是在还有一個單獨的線程中執行的。而不是在創建它們的線程中執行的。也就是說,你在主線程中創建了一個代碼塊并將之提交給 Dispatch 队列,但全部的任務(代碼塊)都不在主線程而是在另外的線程中運行的。

串行隊列

當創建一個串行隊列時。這個隊列一次仅仅能執行一個任務。

在同一個串行隊列中的任務會和平共處并以串行的方式執行。可是并沒有規定全部任務都仅仅能在一個串行隊列中執行,你仍然能够通過使用多個串行隊列的方式来並行地執行任務。

舉個样例,你能够同一时候創建兩個串行隊列。雖然每個隊列一次仅仅執行一個任務,但總體效果就是每次有兩個任務在異步地執行。

串行隊列非常利於處理共享資源。它能保證對共享資源的访问是以串行的方式進行的并有效避免了競爭條件。想像一下,仅仅有一個售票窗体,卻有一堆想買電影票的人,那麼坐在售票窗体處的那個售票員就是一個共享資源。

假设這個售票員同時為這麼多人提供服務,那麼能够預料現場將是何等的混亂。要解決這個問題,人們须要排隊(串行隊列),那麼售票員一次仅仅须要給一個人賣票就能够了。

可是,并非說電影院一次仅仅能賣給一張票給顧客。

假设添加兩個售票窗体。電影院就能够一次向三個人賣票。這就是我所說的,你仍然能够使用多個串行隊列並行地執行多個任務。

使用串行隊列的好處包含:

  1. 保證對共享資源的順序访問,避免競爭條件
  2. 任務以預定的順序執行。當你向串行隊列提交任務后。任務將以他們加入的順序執行。

  3. 你能够創建多個串行隊列。

並行隊列

顧名思義,並行隊列允許你以並行的方式執行多個任務。這些任務(代碼塊)一开始的順序是它們加入時的順序,但它們執行時都是並行的,它們不须要等待其他任務。並行隊列仅仅能保證任務一开始的順序是加入時的順序,但無法知道它們的執行順序、執行時間或者某一時刻被執行的任務數。

比如。你提交了三個任務(任務 #1,#2,#3)到同一個並行隊列。

這些任務會以並行的方式執行,它們啟動的順序就是它們加入隊列的順序。可是它們的執行時間和完毕時間是不盡同样的。

甚至可能任務 #2、#3 在 #1 之後啟動了,卻在任務 #1 之前完毕。任務的執行由系統決定。

使用隊列

我們已經介紹了串行隊列和並行隊列,現在來看看怎样使用它們。

默認,系統會提供給每個 App 一個串行隊列和四個並行隊列。主 dispatch 隊列是全局的串行隊列,用於執行主線程中的任務。主 Dispatch 隊列通经常使用於更新 App 的用戶界面。并執行全部與 UIView 的顯示有關的任務。

它一次仅仅會執行一個任務,因此當你在主 Dispatch 隊列中運行繁重任務時,UI 會被堵塞。

除了主 Dispatch 隊列,系統還提供了四個並行隊列。

我們稱之為全局 Dispatch 隊列。這些隊列在 App 範圍內是全局的,但它們的優先級各不同样。要使用這些隊列。你须要用 dispatch_get_global_queue 函數獲得一個指定類型隊列的引用。這個函數的第一個参數能够采用以下四個值:

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

這些值分別指定了並行隊列的四個不同的優先級。HIGH 表示優先級最高,BACKGROUND 表示優先級最低。因此你须要根據任務的優先程度來指定要使用的隊列。

注意。這些隊列同時也被蘋果的 API 所使用,因此這些隊列中並不僅僅仅仅有你的自己的任務。

最後,你還能够創建随意數量的串行和並行隊列。對於並行隊列,我強烈建議你使用上述四個全局隊列就能够了,當然,你也能够創建另外的隊列。

GCD 速查表

現在,你已經基本了解了 Dispatch 隊列。

為了便於参考。我列出一個簡單的速查表。這個速查表非常簡單,但列表中包含了你應該掌握的全部與 GCD 有關的知識點。

技术分享

不錯吧?現在讓我們創建一個小小的 Demo。以示範 Dispatch 隊列的使用方法。

我將演示怎样使用 Dispatch 隊列來優化 App 的性能,并使 App 更加“響應式”。

演示样例項目

我們的起始項目非常簡單。仅仅是顯示四個 Image View,每個 Image View 都會從 Web 上請求一張圖片。

Web 請求放在了主線程中。為了演示 UI 在響應性能上所受的影響。我在這些 Image View 以下放了一個滑動條。現在下載并運行起始項目。

點擊 Start 按鈕,开始下載圖片。在下載過程中,拖動滑動條。

你會發現滑動條根本無法拖動。

技术分享

當你點擊 Start 按鈕后,圖片开始在主線程中下載。顯然,這種方式非常不好并導致了 UI 停止響應。不幸的是。直到眼下為止,仍然有一些 App 以這種方式在主線程中進行繁重的加載操作。

接下來,我們將以 Dispatch 隊列來解決這個問題。

我們會先用并行隊列的方式然後再用串行隊列的方式解決這個問題。

使用並行 Dispatch 隊列

在 Xcode 中打開 ViewController.swift 文件。假设你看過代碼。你會發現一個叫 didClickOnStart 的 Action 方法。

這個方法用於圖片的下載。在這個方法中我們是這樣做的:

@IBAction func didClickOnStart(sender: AnyObject) {
    let img1 = Downloader.downloadImageWithURL(imageURLs[0])
    self.imageView1.image = img1

    let img2 = Downloader.downloadImageWithURL(imageURLs[1])
    self.imageView2.image = img2

    let img3 = Downloader.downloadImageWithURL(imageURLs[2])
    self.imageView3.image = img3

    let img4 = Downloader.downloadImageWithURL(imageURLs[3])
    self.imageView4.image = img4

}

每個 downloader 都是一個下載任務。同時全部任務眼下都是在主線程中操作的。現在,我們從全局的並行隊列中獲得一個默認優先級的隊列。

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
        dispatch_async(queue) { () -> Void in

            let img1 = Downloader.downloadImageWithURL(imageURLs[0])
            dispatch_async(dispatch_get_main_queue(), {

                self.imageView1.image = img1
            })

        }

首先獲得默認優先級的並行隊列。并將其引用到變量 dispatch_get_global_queue

在這代碼塊中。我們提交了一個任務用於下載第一個圖片。當圖片下載完毕,又向主隊列中提交还有一個任務用於將下載好的圖片顯示到 Image View 中。也就是說,我們將圖片下載任務放到了後臺線程中,而將 與 UI 有關的任務放到了主隊列中。

將剩下的圖片以同樣方式下載,代碼最終變成這樣:

@IBAction func didClickOnStart(sender: AnyObject) {

    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    dispatch_async(queue) { () -> Void in

        let img1 = Downloader.downloadImageWithURL(imageURLs[0])
        dispatch_async(dispatch_get_main_queue(), {

            self.imageView1.image = img1
        })

    }
    dispatch_async(queue) { () -> Void in

        let img2 = Downloader.downloadImageWithURL(imageURLs[1])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView2.image = img2
        })

    }
    dispatch_async(queue) { () -> Void in

        let img3 = Downloader.downloadImageWithURL(imageURLs[2])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView3.image = img3
        })

    }
    dispatch_async(queue) { () -> Void in

        let img4 = Downloader.downloadImageWithURL(imageURLs[3])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView4.image = img4
        })
    }

}

我們將四張圖片的下載以並行方式提交到默認隊列中。現在運行程序,它會運行得更快(假设你遇到不论什么錯誤,檢查你的代碼是否和上述代碼一致)。注意在下載圖片的同時,你還能够流利地拖動滑動條。

使用串行 Dispatch 隊列

我們還能够用串行隊列來解決這個問題。

依旧是 ViewController.swift 文件里的 didClickOnStart() 方法。這次我們用串行隊列方式來下載圖片。在使用串行隊列的時候。我們须要特別注意你當前所引用的是哪一個串行隊列。

每個 App 都會有一個默認的串行隊列。即 UI 更新所使用的主隊列。

因此要注意,在使用串行隊列時,我們必須創建一個新的串行隊列。否則我們的任務會在 App 執行更新 UI 的時候執行你的任務。這將導致錯誤和卡頓感出現。導致用戶體驗變差。我們能够用 dispatch_queue_create 函數創建新隊列,并曾经面的方式來提交任務。改动后的代碼例如以下所看到的:

@IBAction func didClickOnStart(sender: AnyObject) {

    let serialQueue = dispatch_queue_create("com.appcoda.imagesQueue", DISPATCH_QUEUE_SERIAL)


    dispatch_async(serialQueue) { () -> Void in

        let img1 = Downloader .downloadImageWithURL(imageURLs[0])
        dispatch_async(dispatch_get_main_queue(), {

            self.imageView1.image = img1
        })

    }
    dispatch_async(serialQueue) { () -> Void in

        let img2 = Downloader.downloadImageWithURL(imageURLs[1])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView2.image = img2
        })

    }
    dispatch_async(serialQueue) { () -> Void in

        let img3 = Downloader.downloadImageWithURL(imageURLs[2])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView3.image = img3
        })

    }
    dispatch_async(serialQueue) { () -> Void in

        let img4 = Downloader.downloadImageWithURL(imageURLs[3])

        dispatch_async(dispatch_get_main_queue(), {

            self.imageView4.image = img4
        })
    }

}

如你所見,唯一不同的事情僅僅是把並行隊列換成了串行隊列。當再次運行程序,你會發現圖片在後臺下載,你仍然能够和 UI 進行交互。

但有兩個地方须要注意:

  1. 相對於並行隊列來說,下載圖片的時間會有一定的延長。因為我們一次仅仅會下載一張圖片。每個任務都得等上一個任務完毕。
  2. 圖片下載的順序依序為 image1,image2,image3,image4。

    因為串行隊列一次仅仅執行一個任務。

第二部份: Operation Queue

GCD 是一種底層的 C 語言 API,它允許開發者以並行方式執行任務。

而 Operation 隊列相對來說是一種更高級和抽象的隊列模型。產生於 GCD 之上。換句話說,你能够像 GCD 一樣執行並行任務,但卻能够使用面向對象的方式。也就是說,Operation 隊列讓仅仅會讓開發者更加輕鬆。

但與 GCD 不同。Operation 隊列不遵從先進先出的原則。

這裡列出了二者的不同之處:

  1. 不遵循先進先出原則:在 Operation 隊列中,你能够為任務設定執行的優先級并為任務之間加入依賴性,也就是說,你能够讓一些任務總是在其他任務執行完之後再執行。這就是它們不须要遵循先進先出原則的原因。

  2. 默認,任務以並行方式執行:你無法將任務以串行方式執行,當然,你能够通過設置任務之間的依賴的方式,讓 Operation 隊列以某種順序執行任務。
  3. Operation 隊列是 NSOperationQueue 類的實例,它的任務就是作為 NSOperation 對象的容器。

NSOperation

準備提交給 Operation 隊列的任務必須以 NSOperation 實例的方式進行封裝。

我們介紹過 GCD 的任務是以塊的形式進行提交。類似地。提交給 Operation 隊列的任務必須封裝在 NSOperation 對象中。你能够簡單地將 NSOperation 視作一個單獨的任務單元。

NSOperation 是一個抽象類。它無法直接使用,因此我們必須對它進行子類化。在 iOS SDK 中,提供了兩種 NSOperation 子類實現。這兩個類能夠直接使用。但你仍然能够直接對 NSOperation 進行子類化,創建自己的子類來執行任務。能够直接使用的兩個子類分別是:

  1. NSBlockOperation - 這個類用一個或多個塊創建。它能够包含不止一個塊,仅仅有當全部塊的代碼都執行完才視作該任務完毕。
  2. NSInvocationOperation - 這個類創建出的 NSOperation 可用於執行指定對象的選擇器(Selector)。

但使用 NSOperation 有什麼好處?

  1. 首先,它們能够用 NSOperation 類的 addDependency(op:NSOperation) 方法來加入依賴。當你须要依賴一個任務的執行結果來啟動还有一個任務時,你就能够用 NSOperation 了。
  2. 技术分享
  3. 其次,你能够通過 queuePriority 屬性來改變一個 Operation 的優先級,該屬性可能的取值包含:
    public enum NSOperationQueuePriority : Int {
        case VeryLow
        case Low
        case Normal
        case High
        case VeryHigh
    }
    高優先級的任務將優先執行。
  4. 你能够取消指定隊列的全部或單個 Operation。

    在將一個 Operation 加入隊列后你又能够取消它。在該 Operation 上調用 cancel() 方法就可以取消該 Operation。

    在取消一個 Operation 時,會碰到三種情形:

    • 你的 Operation 已經完毕。

      這種情形下, cancel 方法什麼也不做。

    • 你的 Operation 正在執行。在這種情形下,系統不會強行終止 Operation 中的代碼。但會將 cancelled 屬性設置為 true。
    • Operantion 已經位於隊列中。處於等待執行狀態。在這種情形下。這個 Operation 不會被執行。
  5. NSOperation 有三個实用的布爾屬性,分別是 finished、cancelled 和 ready。當 Operation 被執行完,finished 就會設置為 true。當 Operation 被取消,cancelled 就被設置為 true。

    當 Operation 即將要被執行時。ready 就被設置為 true。

  6. 不论什么 NSOperation 對象都可設置一個可選的完毕塊,當任務完毕時會調用這個完毕塊。

    當 finished 屬性一設置為 true 后,马上調用該完毕塊。

現在。讓我們改动我們的代碼。讓它用 NSOperationQueue 來又一次實現。首先在 ViewController 中聲明一個變量:

var queue = NSOperationQueue()

然後。將 didClickOnStart 方法改动為例如以下代碼。這段代碼中顯示了怎样在 NSOperationQueue 中操作 Operation:

@IBAction func didClickOnStart(sender: AnyObject) {
    queue = NSOperationQueue()

    queue.addOperationWithBlock { () -> Void in

        let img1 = Downloader.downloadImageWithURL(imageURLs[0])

        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView1.image = img1
        })
    }

    queue.addOperationWithBlock { () -> Void in
        let img2 = Downloader.downloadImageWithURL(imageURLs[1])

        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView2.image = img2
        })

    }

    queue.addOperationWithBlock { () -> Void in
        let img3 = Downloader.downloadImageWithURL(imageURLs[2])

        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView3.image = img3
        })

    }

    queue.addOperationWithBlock { () -> Void in
        let img4 = Downloader.downloadImageWithURL(imageURLs[3])

        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView4.image = img4
        })

    }
}

如上述代碼所看到的。我們使用 addOperationWithBlock 方法以指定的塊(或者叫做 Swift 閉包)來創建一個新的 Operation 實例。非常簡單,是吧?要在主隊列中執行任務,我們用 NSOperationQueue 的 mainQueue() 方法取代 GCD 中的 dispatch_async() 方法來獲取主隊列,然後將要在主隊列中執行的操作提交給主隊列。

你能够運行程序來試一試。假设代碼正確, App 將在後臺下載圖片,同時不會堵塞 UI。

在前面的样例中。我們使用 addOperationWithBlock 方法將 Operation 加入進隊列中。接下來我們看看怎样使用 NSBlockOperation 來做同樣的事情。

同時。我們能够擁有很多其他的選項和功能,比方為 Operation 設置一個完毕塊。將 didClickOnStart 方法改动為:

@IBAction func didClickOnStart(sender: AnyObject) {

    queue = NSOperationQueue()
    let operation1 = NSBlockOperation(block: {
        let img1 = Downloader.downloadImageWithURL(imageURLs[0])
        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView1.image = img1
        })
    })

    operation1.completionBlock = {
        print("Operation 1 completed")
    }
    queue.addOperation(operation1)

    let operation2 = NSBlockOperation(block: {
        let img2 = Downloader.downloadImageWithURL(imageURLs[1])
        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView2.image = img2
        })
    })

    operation2.completionBlock = {
        print("Operation 2 completed")
    }
    queue.addOperation(operation2)


    let operation3 = NSBlockOperation(block: {
        let img3 = Downloader.downloadImageWithURL(imageURLs[2])
        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView3.image = img3
        })
    })

    operation3.completionBlock = {
        print("Operation 3 completed")
    }
    queue.addOperation(operation3)

    let operation4 = NSBlockOperation(block: {
        let img4 = Downloader.downloadImageWithURL(imageURLs[3])
        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.imageView4.image = img4
        })
    })

    operation4.completionBlock = {
        print("Operation 4 completed")
    }
    queue.addOperation(operation4)
}

對於每個 Operation。我們都創建了一個新的 NSBlockOperation 用於將要執行的任務封裝到塊中。通過 NSBlockOperation,我們還能够設置它的完毕塊。

當 Operation 執行完后,完毕塊將被調用。為求簡便。我們僅僅在完毕塊中輸出了一個簡單消息表示 Operation 執行完畢。假设你再次運行程序,你將看到控制台中輸出了例如以下內容:

Operation 1 completed
Operation 3 completed
Operation 2 completed
Operation 4 completed

取消 Operation

正如我們前面提到的。NSBlockOperation 允許你管理 Operation。

接下來我們看一看怎样取消 Operation。首先。在導航條中加入一個取消按鈕。標題為 Cancel。為了演示 Operation 的取消,我們會在 Operation #2 和 Operation #1 之間、Operation #3 和 Operation #2 之間各自加入一個依賴關係。

也就是說,Operation #2 會等待 Operation #1 執行完之後才執行,Operation #3 會等待 Operation #2 執行完之後才執行。Operation #4 則沒有依賴。它是正常的異步操作。要取消全部 Operation,你仅仅需調用 NSOperationQueue 的 cancelAllOperations() 方法。在 ViewController 中新增例如以下方法:

   @IBAction func didClickOnCancel(sender: AnyObject) {

        self.queue.cancelAllOperations()
    }

當然,你须要在導航欄中新加入的 Cancel 按鈕和 didClickOnCancel 方法之間創建一個連接。這须要我們回到 Main.storyboard 并使用連接面板。在連接面板中。你將看到 Received Actions 一欄以下有一個未連接的 didSelectCancel() 方法。

點擊 + 按鈕,從小圓圈拖一條線到 Cancel 按鈕。

然後在 didClickOnStart 方法中添加例如以下語句以創建依賴:

operation2.addDependency(operation1)
operation3.addDependency(operation2)

然後改动 Operation #1 的完毕塊中的日誌打印語句為:

operation1.completionBlock = {
            print("Operation 1 completed, cancelled:\(operation1.cancelled) ")
        }

同樣,改动 Operation #2、#3 和 #4 的日誌語句,以便我們能理解整個流程。

現在運行程序。點擊 Start 按鈕后,马上點擊 Cancel 按鈕。

這將在 Operation #1 執行完之後取消全部的 Operation。詳細步驟例如以下:

  • 因為 Operation #1 已經开始執行,取消動作將對它毫無影響。因此它的 cancelled 值打印出來就是 false。App 將如圖 1 所顯示。
  • 假设你點擊 Cancel 按鈕的速度夠快,Operation #2 就會被取消。cancelAllOperations() 方法將讓它停止執行,因此第二張圖片不會被下載。
  • Operation #3 已經在隊列中,等待 Operation #2 完毕。因為它依賴於 Operation #2 的完毕而 Operation #2 又被取消了,因此 Operation #3 不會被執行,并马上被移出隊列。

  • Operation #4 沒有設置依賴。它被異步執行,并下載了第四張圖片。

技术分享

接下來做什麼?

在本教程中,我完整地介紹了 iOS 異步編程的概念,以及怎样在 iOS 中實現異步。我詳細地介紹了什麼是異步。GCD 以及怎样創建串行隊列和並行隊列。同時我們也介紹了 NSOperationQueue。你也了解到了 GDC 和 NSOperationQueue 究竟有什麼不同。

要進一步了解 iOS 異步編程。我建議你去讀 蘋果的異步編程指南

作為参考,你還能够從 Github 上下載本教程中的完整源代碼:iOS 異步編程代碼.

對本文有不论什么疑問請留言。衷心希望你能對本文發表不论什么評論。

譯者簡介

楊宏焱,男。中國大陸籍人士,CSDN 博客專家(個人博客 http://blog.csdn.net/kmyhy)。

2009 年开始學習蘋果 iOS 開發。精通 O-C/Swift 和 Cocoa Touch 框架。開發有多個商店應用和企業 App。

熱愛寫作。著有多本技術專著。包含:《企業級 iOS 應用實戰》、《iPhone & iPad 企業移動應用開發秘笈》、《iOS8 Swift 編程指南》,《寫給大忙人看的 Swift》(合作翻譯)等。

iOS 並行編程初步

标签:取消   oss   obj   tty   finish   reference   操作   基本   售票   

原文地址:http://www.cnblogs.com/jzdwajue/p/7242959.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!