日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
GrandCentralDispatch教程

在教程的***部分,你學到了一些關于并發(fā),線程及GCD工作原理的知識。你通過并用dispatch_barrier_async與dispatch_sync保證了PhotoManager單例在讀取與寫入照片過程中的線性安全性。值得一提的是,你不僅通過dispatch_after及時地向用戶發(fā)出提醒以此優(yōu)化了App的UX而且還通過dispatch_async將部分工作從一個View Controller的實例化過程中分割至另一線程以此實現CPU高密度處理工作。

專業(yè)成都網站建設公司,做排名好的好網站,排在同行前面,為您帶來客戶和效益!成都創(chuàng)新互聯為您提供成都網站建設,五站合一網站設計制作,服務好的網站設計公司,成都網站設計、成都網站建設負責任的成都網站制作公司!

假如你是一路從上一部分教程學過來的話,你完全可以在以前的工程文件上繼續(xù)Coding。但假如你沒有完成教程的***部分或是不想繼續(xù)使用自己的工程文件的話,你可從這里下載到教程***部分的完整工程文件。

OK! 是時候探索一下更多關于GCD的知識了。

修復提早出現的Popup

也許你已經注意到了當你通過Le Internet的方式添加照片時,在所有照片下載完成前AlertView就已經跳出來提醒你“Download Complete”。

See That?

其實問題出在PhotoManaer的downloadPhotosWithCompletion函數中:

 
 
  1. func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { 
  2. var storedError: NSError! 
  3. for address in [OverlyAttachedGirlfriendURLString, 
  4. SuccessKidURLString, 
  5. LotsOfFacesURLString] { 
  6. let url = NSURL(string: address) 
  7. let photo = DownloadPhoto(url: url!) { 
  8. image, error in 
  9. if error != nil { 
  10. storedError = error 
  11. PhotoManager.sharedManager.addPhoto(photo) 
  12. if let completion = completion { 
  13. completion(error: storedError) 

在函數結尾部分調用completion閉包--這就代表著你認為所有的照片的下載任務都已經完成。但不幸的是此時此刻你并無法保證所有的下載任務都已經完成。

DownloadPhoto類的實例化方法開始從一個URL下載文件并在下載完成前立即返回值。換句話說,downloadPhotosWithCompletion在函數結尾處調用其自己的completion閉包就好像它自己就是個有著直線型同步執(zhí)行代碼的方法體,并且每個方法執(zhí)行完自己的工作后都會調用這個completed。

不管怎樣,DownloadPhoto(url:)是異步執(zhí)行的并且立即返回--所以這個解決方案不管用。

再有,downloadPhotosWithCompletion應該在所有的照片下載任務都調用了completion閉包后再調用自己的completion閉包。那么問題來了:你怎樣去監(jiān)管那些同時執(zhí)行的異步事件呢?你根本不會知道它們會何時并以何種順序結束。

或許你可以寫多個Bool值去跟蹤每個任務的下載狀態(tài)。說實話,這樣做的話會感覺有些low并且代碼看起來會很亂。

萬幸的是,派發(fā)組(dispatch groups)正是為滿足監(jiān)管這種多異步completion的需要所設計的。

派發(fā)組(Dispatch Group)

當整組的任務都完成時派發(fā)組會提醒你。這些任務既可以是異步的也可以是同步的并且可以在不同隊列中被監(jiān)管。當全組任務完成時派發(fā)組可以通過同步或異步的方式提醒你。只要有任務在不同隊列中被監(jiān)管,dispatch_group_t實例便會在多個隊列中的持續(xù)監(jiān)管這些不同的任務。

當組中的全部任務執(zhí)行完畢后,GCD的API提供兩種方式向你發(fā)出提醒。

***個便是dispatch_group_wait,這是一個在組內所有任務執(zhí)行完畢前或在處理超時的情況下限制當前線程運行的函數。在AlertView提早出現的情況下,使用disptach_group_wait絕對是你的***解決方案。

打開PhotoManager.swift并用如下代碼替換原downloadPhotosWithCompletion函數:

 
 
  1. func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { 
  2. dispatch_async(GlobalUserInitiatedQueue) { // 1 
  3. var storedError: NSError! 
  4. var downloadGroup = dispatch_group_create() // 2 
  5. for address in [OverlyAttachedGirlfriendURLString, 
  6. SuccessKidURLString, 
  7. LotsOfFacesURLString] 
  8. let url = NSURL(string: address) 
  9. dispatch_group_enter(downloadGroup) // 3 
  10. let photo = DownloadPhoto(url: url!) { 
  11. image, error in 
  12. if let error = error { 
  13. storedError = error 
  14. dispatch_group_leave(downloadGroup) // 4 
  15. PhotoManager.sharedManager.addPhoto(photo) 
  16. dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // 5 
  17. dispatch_async(GlobalMainQueue) { // 6 
  18. if let completion = completion { // 7 
  19. completion(error: storedError) 

代碼分步解釋:

一旦使用限制當前線程運行的同步式dispatch_group_wait,你必須用dispatch_async將整個方法調至后臺隊列以保證主線程的正常運行。

在這里聲稱了一個你可以將其視為未完成任務計數器的新派發(fā)組。

dispatch_group_enter用來向派發(fā)組提醒新任務執(zhí)行的開始。你必須使調用dispatch_group_enter的次數要相稱于調用dispatch_group_leave的次數,不然將會導致App崩潰。

在這里,你向派發(fā)組提醒任務的執(zhí)行結束。再強調一遍,進出派發(fā)組的次數一定要相等。

在所有任務執(zhí)行結束后或者在處理超時的情況下dispatch_group_wait才會執(zhí)行。假如在所有任務執(zhí)行結束前就出現了處理超時的情況,函數便會返回一個非零結果。你可以將其放在一個特殊的閉包中以檢查是否會發(fā)生處理超時的情況。當然,在本教程的情況下你可以使用DISPATCH_TIME_FOREVER令其保持等待請求狀態(tài),這就意味它會一直等下去,因為照片的下載任務總會完成的。

到目前為止,你保證了照片下載任務要么順利完成要么出現處理超時的情況。其后你便可以返回至主隊列運行你的completion閉包。這將向主線程添加稍后將被執(zhí)行的任務。

條件允許的情況下執(zhí)行completion閉包。

編譯并運行你的App,你會發(fā)現在點擊下載照片的選項后你的completion閉包將會在正確的時間執(zhí)行。

提醒:當你在實體設備上運行App的時候,假如網絡機制運行過快以至于你無法判斷的completion閉包開始執(zhí)行時間的話,你可以到App的Setting中的Developer Section中進行一些網絡調整。打開Network Link Conditioner,選擇Very Bad Network是一個不錯的選擇。

假如你在模擬器上運行App的話,你可以通過使用Network Link Conditioner included in the Hardare IO Tools for Xcode調整你的網絡速度。這是一個當你需要了解在網絡狀況不好的情況下App執(zhí)行情況的***工具。

這個解決方法的好處不止于此,但總體上來說它在大多數情況下避免了限制線程正常運行的可能。你接下來的任務便是寫一個相同的并以異步的方式向你發(fā)出'照片下載完成'提醒的方法。

在開始之前先了解一下對于不同類型的隊列來說應該何時使用并怎樣使用派發(fā)組的簡短教程。

自定義連續(xù)隊列(Custom Serial Queue): 在組內任務全部完成時需要發(fā)出提醒的情況下,自定義連續(xù)隊列是一個不錯的選擇。

主隊列(Main Queue[Serial]):在當你以同步的方式等待所有任務的完成且你也不想限制主隊列的運行的情況下你應該在主線程上警惕使用它。但比如像網絡請求這種長時間運行的任務結束時異步模型是用來更新UI的***方式。

并發(fā)隊列(Concurrent Queue):這對于派發(fā)組及完成提醒也是個不錯的選擇。

派發(fā)組,再來一次!

出于精益求精的目的,通過異步的方式將下載任務派發(fā)到另一個隊列并用dispatch_group_wait限制其運行的做法是不是有些stupid呢?試試另一種方法吧...

用如下實現代碼代替PhtotManager.swift中的downloadPhotosWithCompletion函數:

 
 
  1. func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { 
  2. // 1 
  3. var storedError: NSError! 
  4. var downloadGroup = dispatch_group_create() 
  5. for address in [OverlyAttachedGirlfriendURLString, 
  6. SuccessKidURLString, 
  7. LotsOfFacesURLString] 
  8. let url = NSURL(string: address) 
  9. dispatch_group_enter(downloadGroup) 
  10. let photo = DownloadPhoto(url: url!) { 
  11. image, error in 
  12. if let error = error { 
  13. storedError = error 
  14. dispatch_group_leave(downloadGroup) 
  15. PhotoManager.sharedManager.addPhoto(photo) 
  16. dispatch_group_notify(downloadGroup, GlobalMainQueue) { // 2 
  17. if let completion = completion { 
  18. completion(error: storedError) 

這就是你的新異步方法的工作原理:

在這個新的實現方法中,當你不再限制主線程的時候你就沒有必要將其放進dispatch_async的調用中。

dispatch_group_notify相當于一個異步completion閉包。當派發(fā)組中不再剩余任何任務且輪到completion閉包運行時,這段代碼將會執(zhí)行。你也可以定義你的completion代碼在哪個隊列上運行。在這段代碼中你便要運行在主隊列上。

對于在不限制任何線程運行的情況下處理這種特殊需求的例子來說,這是一種較為簡潔的方式。

過多使用并發(fā)機制造成的危險

學了這么多的新東西后,你是不是該令你的代碼全部實現線程化呢?

Do It !!!

看看你在PhotoManger中的downloadPhotosWithCompletion函數。你應該注意到了那個循環(huán)在三個參數間并下載三張不同照片的for循環(huán)。你接下來的工作便是嘗試通過并發(fā)機制加快for循環(huán)的運行速度。

該輪到dispatch_apply上場了。

dispatch_apply就像是一個以并發(fā)的形式執(zhí)行不同迭代過程的for循環(huán)。就像是一個普通的for循環(huán),dispatch_apply是一個同步運行且所有工作完成后才會返回的函數。

當你在對閉包內已給定任務的數量進行***化迭代過程數量的設定時一定要當心,因為這種存在多個迭代過程且每個迭代過程僅包含少量工作的情況所消耗的資源會抵消掉并發(fā)調用所產生的優(yōu)化效果。這個叫做striding的技術會在你處理多任務的每個迭代過程的地方幫到你。

什么時候適合用dispatch_apply呢?

自定義連續(xù)隊列(Custome Serial Queue):對于連續(xù)隊列來說,dispatch_apply沒什么用處;你還是老實地用普通的for循環(huán)吧。

主隊列(Main Queue[Serial]):跟上面情況一樣,老實地用普通for循環(huán)。

并發(fā)隊列(Concurrent Queue):當你需要監(jiān)管你的任務處理進程時,并發(fā)循環(huán)絕對是一個好主意。

回到downloadPhotosWithCompletion并替換成如下代碼:

 
 
  1. func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { 
  2. var storedError: NSError! 
  3. var downloadGroup = dispatch_group_create() 
  4. let addresses = [OverlyAttachedGirlfriendURLString, 
  5. SuccessKidURLString, 
  6. LotsOfFacesURLString] 
  7. dispatch_apply(addresses.count, GlobalUserInitiatedQueue) { 
  8. i in 
  9. let index = Int(i) 
  10. let address = addresses[index] 
  11. let url = NSURL(string: address) 
  12. dispatch_group_enter(downloadGroup) 
  13. let photo = DownloadPhoto(url: url!) { 
  14. image, error in 
  15. if let error = error { 
  16. storedError = error 
  17. dispatch_group_leave(downloadGroup) 
  18. PhotoManager.sharedManager.addPhoto(photo) 
  19. dispatch_group_notify(downloadGroup, GlobalMainQueue) { 
  20. if let completion = completion { 
  21. completion(error: storedError) 

你的循環(huán)塊現在就是以并發(fā)的形式運行;在上述代碼中,你為調用dispatch+apply提供了三個參數。***參數聲明了迭代過程的數量,第二個參數聲明了將要運行多個任務的隊列,第三個參數聲明了閉包。

要知道盡管你已經有了在線程安全模式下添加照片的代碼,但是照片順序會根據***完成的線程的順序所排列。

編譯并運行,通過Le Internet的方式添加一些照片。注意到有什么不同嗎?

在真實設備上運行修改后的代碼偶爾會運行得快一些。所以,上面做出的修改值得嗎?

其實,在這種情況下它不值得你這么做。原因如下:

你已經調用出了比for循環(huán)再同種情況下消耗更多資源的線程。dispatch_apply在這里顯得有些小題大做了。

你寫App的時間是有限的--不要為那些'抓雞不成蝕把米'的優(yōu)化代碼浪費時間,把你的時間用在優(yōu)化得有明顯效果的代碼上。你可以選擇使用Xcode中的Instruments來測試出你App中執(zhí)行時間最長的方法。

在某些情況下,優(yōu)化后的代碼甚至會增加你和其他開發(fā)者理解其邏輯結構的難度,所以優(yōu)化效果一定要是物有所值的。

記住,不要癡迷于優(yōu)化,要不然你就是和自己過不去了。

取消派發(fā)塊(dispatch block)的執(zhí)行

要知道在iOS 8和OS X Yosemite中加入了名為dispatch block objects的新功能(中文叫‘派發(fā)塊對象’感覺總是怪怪的,所以就繼續(xù)用英文原名)。Dispatch block objects可以做不少事兒了,比如通過為每個對象設定一個QoS等級來決定其在隊列中的優(yōu)先級,但它最特別的功能便是取消block objects的執(zhí)行。但你需要知道的是一個block object只有在到達隊列頂端且開始執(zhí)行前才能被取消。

咱們可以通過‘利用Le Internet開始并再取消照片下載任務’的方式詳細描述取消Dispatch Block的運行機制。用下述代碼代替PhotoManager.swift中的downloadPhotosWithCompletion函數:

 
 
  1. func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { 
  2. var storedError: NSError! 
  3. let downloadGroup = dispatch_group_create() 
  4. var addresses = [OverlyAttachedGirlfriendURLString, 
  5. SuccessKidURLString, 
  6. LotsOfFacesURLString] 
  7. addresses += addresses + addresses // 1 
  8. var blocks: [dispatch_block_t] = [] // 2 
  9. for i in 0 ..< addresses.count { 
  10. dispatch_group_enter(downloadGroup) 
  11. let block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) { // 3 
  12. let index = Int(i) 
  13. let address = addresses[index] 
  14. let url = NSURL(string: address) 
  15. let photo = DownloadPhoto(url: url!) { 
  16. image, error in 
  17. if let error = error { 
  18. storedError = error 
  19. dispatch_group_leave(downloadGroup) 
  20. PhotoManager.sharedManager.addPhoto(photo) 
  21. blocks.append(block) 
  22. dispatch_async(GlobalMainQueue, block) // 4 
  23. for block in blocks[3 ..< blocks.count] { // 5 
  24. let cancel = arc4random_uniform(2) // 6 
  25. if cancel == 1 { 
  26. dispatch_block_cancel(block) // 7 
  27. dispatch_group_leave(downloadGroup) // 8 
  28. dispatch_group_notify(downloadGroup, GlobalMainQueue) { 
  29. if let completion = completion { 
  30. completion(error: storedError) 

addresses數組內包含每個都被復制了三份的address變量。

這個數組包含著晚些時候將被使用的block objects。

dispatch_block_create聲明了一個新的block object。***個參數是定義了不同block特性的標志。這個標志使得block繼承了其在被分配至隊列中的QoS等級。第二個參數是以一個閉包形式定義的block。

這是一個以異步形式分發(fā)到全局主隊列的block。在這個例子中所使用的主隊列是一個連續(xù)隊列,所以其更容易取消所選的blocks。定義了分發(fā)blocks的代碼已在主隊列上的執(zhí)行保證了下載blocks的稍后執(zhí)行。

除去前三次的下載,在剩余的數組元素中執(zhí)行for循環(huán)。

arc4random_uniform提供一個在0至上限范圍內(不包含上限)的整數。就像擲硬幣那樣,將2設定為上限后你將會得到0或1中的某一個整數。

假如在隨機數是1、block還在隊列中且沒有正在被執(zhí)行的情況下,block則被取消。在執(zhí)行過程中的block是不能被取消的。

當所有blocks都被加入分發(fā)隊列后,不要忘記刪除被取消的隊列。

編譯并運行App,通過Le Internet 的方式添加照片。你會發(fā)現App在下載了原來的三張照片后還會再下載一個隨機數量的照片。分配至隊列后,剩余的blocks將被取消。盡管這是一個很牽強的例子但起碼它很好的描述了dispatch block objects如何被使用或被取消的。

Dispatch block objects能做的還有很多,使用前別忘了看看官方文檔。

GCD帶來的各種各樣的樂趣

且慢!再容我講點兒東西!其實這還有些不常用的函數,但在特殊情況下它們是非常有用的。

測試異步代碼

這也許聽起來有些不靠譜,但你知道Xcode上的確有這項測試功能嗎?:]其實在某些情況下我是假裝不知道有這項功能的,但是在處理具有復雜關系的代碼的時候,代碼編寫與運行的測試是非常重要的。

Xcode中的測試功能是以XCTestCase的子類形式出現其且在其中運行的任何方法都是以test開頭出現的。測試功能在主線程上運行,所以你可以假設每個測試都是以一種連續(xù)(serial)的方式運行的。

只要一個給定的測試方法完成了執(zhí)行,XCTest方法就會認定一個測試已經完成并開始下一個測試,這就意味著在新的測試運行的同時,上一個測試中的異步代碼還會繼續(xù)運行。

當你在執(zhí)行一個網絡請求任務且不想限制主線程的運行時,那么這類網絡任務通常是以異步方式執(zhí)行的。這種“測試方法的結束代表著整個測試過程的結束”的機制加大了網絡代碼測試的難度。

別緊張,接下來咱們看兩個常用的且專門用來測試以異步方式執(zhí)行的代碼的技術:一個使用了信號量(semaphores),另一個使用了期望(expectations)。

信號量(Semaphores)

在很多學校的OS課中,一提到大名鼎鼎的Edsger W.Dijkstra時肯定會講到信號量這個跟線程相關的概念。信號量難懂之處在于它建立在那些復雜的操作系統(tǒng)的函數之上。

假如你想學習更多關于信號量的知識,請到這里了解更多關于信號量理論的細節(jié)。假如你是個專注于學術研究的家伙,從軟件開發(fā)的角度來看,關于信號量的經典例子肯定就是哲學家進餐問題了。

信號量適用于讓你在資源有限的情況下控制多個單位的資源消耗。舉個例子,假如你聲明了一個其中包含兩個資源的信號量,在同一時間內最多只能有兩個線程訪問臨界區(qū)。其他想使用資源的線程必須以FIFO(First Come, First Operate)的順序在隊列中等待。

打開GooglyPuffTests.swift并用如下代碼替換downloadImageURLWithString函數:

 
 
  1. func downloadImageURLWithString(urlString: String) { 
  2. let url = NSURL(string: urlString) 
  3. let semaphore = dispatch_semaphore_create(0) // 1 
  4. let photo = DownloadPhoto(url: url!) { 
  5. image, error in 
  6. if let error = error { 
  7. XCTFail("\(urlString) failed. \(error.localizedDescription)") 
  8. dispatch_semaphore_signal(semaphore) // 2 
  9. let timeout = dispatch_time(DISPATCH_TIME_NOW, DefaultTimeoutLengthInNanoSeconds) 
  10. if dispatch_semaphore_wait(semaphore, timeout) != 0 { // 3 
  11. XCTFail("\(urlString) timed out") 

以下便是信號量如何在上述代碼中工作的解釋:

創(chuàng)建信號量。參數表明了信號量的初始值。這個數字代表著可以訪問信號量線程的數量,我們經常以發(fā)送信號的方式來增加信號量。

你可以在completion閉包中向信號量聲明你不再需要資源了。這樣的話,信號量的值會得到增加并且向其他資源聲明此時信號量可用。

設定信號量請求超時的時間。在信號量聲明可用前,當前線程的運行將被限制。若出現超時的話,函數將會返回一個非零的值。在這種情況下測試便是失敗的,因為它認為網絡請求的返回不該使用超過十秒的時間。

通過使用菜單中的Product/Test選項或者使用?+U快捷鍵測試App的運行。

斷開網絡連接后再次運行測試;假如你在實機上運行就打開飛行模式,若是模擬器的話就斷開鏈接。10秒后這次測試便以失敗告終。

假如你是一個服務器團隊中一員,完成這些測試還是挺重要的。

期望(Expectations)

XCTest框架提供了另一個用來測試異步方式執(zhí)行代碼的解決方案,期望。這便允許你在一個異步任務開始執(zhí)行前設定一個期望--一些你期待發(fā)生的事。在異步任務的期望被標記為已完成(fulfilled)前,你可以令test runner一直保持等待狀態(tài)。

用以下代碼代替GooglyPufftests.swift中的downloadImageWithString函數:

 
 
  1. func downloadImageURLWithString(urlString: String) { 
  2. let url = NSURL(string: urlString) 
  3. let downloadExpectation = expectationWithDescription("Image downloaded from \(urlString)") // 1 
  4. let photo = DownloadPhoto(url: url!) { 
  5. image, error in 
  6. if let error = error { 
  7. XCTFail("\(urlString) failed. \(error.localizedDescription)") 
  8. downloadExpectation.fulfill() // 2 
  9. waitForExpectationsWithTimeout(10) { // 3 
  10. error in 
  11. if let error = error { 
  12. XCTFail(error.localizedDescription) 

解釋一下:

通過expectationWithDescription參數聲明了一個期望。當測試失敗的時候,test runner將會在Log中顯示這段字符串參數,以此代表著你所期待發(fā)生的事情。

調用以異步方式標記期望已完成的閉包中的fulfill.

調用的線程等待期望被waitForExpectationsWithTimeout函數標記完成。若等待超時,線程將被當做一個錯誤。

編譯并運行測試。盡管測試結果跟使用信號量機制比起來并沒有太多的不同,但這卻是一種使XCTest框架更加簡潔易讀的方法。

派發(fā)源(Dispatch Sources)的使用

GCD的一個非常有趣的特性就是派發(fā)源,它是包含了很多低層級別的功能。這些功能可以幫你對Unix的信號,文件描述符,Mach端口、VFS Nodes進行反饋以及檢測。盡管這些東西超出了這篇教程的范圍,但我覺得你還是要試著去實現一個派發(fā)源對象。

很多派發(fā)源的初學者經常被卡在如何使用一個源的的問題上,所以你要清楚dispatch_source_create的工作原理。下面的函數聲明了一個源:

 
 
  1. func dispatch_source_create( 
  2. type: dispatch_source_type_t, 
  3. handle: UInt, 
  4. mask: UInt, 
  5. queue: dispatch_queue_t!) -> dispatch_source_t! 

作為***個參數,type: dispatch_source_type_t決定了接下來的句炳以及掩碼參數的類型。你可以去看一下相關內容的官方文檔以便理解每一種dispatch_source_type_t的用法與解釋。

在這里你將監(jiān)管DISPATCH_SOURCE_TYPE_SIGNAL。如官方文檔里解釋的那樣:派發(fā)源監(jiān)管著當前進程的信號,其句炳的值是一個整數(int),而掩碼值暫時沒有用到而被設為0。

這些Unix信號可以在名為signal.h的頭文件中找到。文件的頂端有很多的#defines。在這堆信號中你將對SIGSTOP信號進行監(jiān)管。當進程收到一個不可避免的暫停掛起指令時這個信號將被發(fā)送。當你用LLDB的debugger除錯的時候同樣的信號也會被發(fā)送。

打開PhotoCollectionViewController.swift文件,在viewDidLoad函數附近添加如下代碼:

 
 
  1. #if DEBUG 
  2. private var signalSource: dispatch_source_t! 
  3. private var signalOnceToken = dispatch_once_t() 
  4. #endif 
  5. override func viewDidLoad() { 
  6. super.viewDidLoad() 
  7. #if DEBUG // 1 
  8. dispatch_once(&signalOnceToken) { // 2 
  9. let queue = dispatch_get_main_queue() 
  10. self.signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, 
  11. UInt(SIGSTOP), 0, queue) // 3 
  12. if let source = self.signalSource { // 4 
  13. dispatch_source_set_event_handler(source) { // 5 
  14. NSLog("Hi, I am: \(self.description)") 
  15. dispatch_resume(source) // 6 
  16. #endif 
  17. // The other stuff 

分布解釋如下:

從安全角度考慮,你***在DEBUG模式下編譯代碼,以防其他人間接地看到你的代碼。: ] 通過在以下路徑,Project Settings -> Build Settings -> Swift Compiler – Custom Flags -> Other Swift Flags -> Debug,通過在Debug選項中添加-D DEBUG的方式來定義DEBUG。

利用dispatch_once對派發(fā)源進行單次的初始化設定。

在這里你實例化了一個signalSource變量并表明你只想進行信號監(jiān)管并將SIGSTOP信號作為第二個參數。再有一點你需要知道的是你使用主隊列處理接收到的事件--至于為什么,你待會兒就會知道了。

假如你提供了一個錯誤的參數,派發(fā)源對象將不會被創(chuàng)建成功??傊?,在使用派發(fā)源對象前你要確定你已經創(chuàng)建了一個可用的派發(fā)源對象。

dispatch_source_set_event_handler注冊了一個在收到特定任務信號時將被調用的事件處理閉包。

在默認的情況下,所有派發(fā)源都是在暫停掛起的狀態(tài)下開始執(zhí)行的。當你打算開始監(jiān)管事件時,你必須讓派發(fā)源對象從新開始運行。

編譯并運行App;以debugger方式暫停App的運行并再立刻恢復運行。檢查一下控制臺,你會看到如下的反饋:

 
 
  1. 2014-08-12 12:24:00.514 GooglyPuff[24985:5481978] Hi, I am: 

在某種程度上你的App現在知道debug了。這非常不錯,但是如何較為實際地使用它呢?

當你從新恢復App運行時你可以使用它對一個object進行debug并顯示其相關數據;當某些不懷好意的人想利用debugger影響App正常運行的時候,你也可以為你的App寫一個自定義安全登錄模塊。

另一個有趣的想法便是通過上述機制實現一個對于debugger中對象的堆棧跟蹤器。

What ?

考慮一下這種情況,你意外的停止了debugger的運行并在很大程度上debugger很難待在預計的棧幀上。但現在你可以任何時間停止debugger的運行并任何地方執(zhí)行代碼。假如你想在App的特定位置中執(zhí)行代碼,上述情況將會非常有用。

在viewDidLoad的事件處理閉包中的NSLog代碼旁添加斷點。在debugger中暫停運行,在恢復運行;你的App將運行至斷點添加處?,F在你便可以隨心所欲地訪問PhotoCollectionViewController中的實例了。

假如你不知道debugger中有哪些線程,可以去查看一下。主線程總會是***個被libdispatch跟隨的線程;GCD的協(xié)調器總會是第二個線程;其他的線程就要視具體情況而定了。

利用斷點功能你可以逐步地更新UI、測試類的屬性甚至在不重新運行App的情況下執(zhí)行特定的方法,看起來很方便吧!

Where to Go From Here?

你可以在這里下載到教程的完整代碼。

我挺不喜歡嘮叨的,但是我覺得你還是應該去看看這篇關于如何使用Xcode中的Instruments的教程。假如你打算對你的App進行優(yōu)化的話,你是絕對會用到Instruments的。要知道Instruments對于推斷相對執(zhí)行的問題是很有用處的:對比不同代碼塊中哪一塊的相對執(zhí)行時間是最長的。

與此同時,你也有必要去看看這篇How to Use NSOperations and NSOperationsQueue Tutorial in Swift的教程。NSOperations可以提供更良好的控制,實現多并發(fā)任務***數量的處理以及在犧牲一定運行速度的情況下使得程序更加面向對象化。

記住!在大多數情況下,除非你有著特殊的原因,一定要盡量使用更高級別的API。只有當你真的想到學到或做到一些非常有趣的事情時再去探索蘋果的Dark Arts吧!

祝你好運!


網站欄目:GrandCentralDispatch教程
網頁路徑:http://m.5511xx.com/article/cdppijj.html