精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Grand Central Dispatch 教程

移動開發 iOS 開發
在教程的第一部分,你學到了一些關于并發,線程及GCD工作原理的知識。你通過并用dispatch_barrier_async與dispatch_sync保證了PhotoManager單例在讀取與寫入照片過程中的線性安全性。

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

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

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閉包就好像它自己就是個有著直線型同步執行代碼的方法體,并且每個方法執行完自己的工作后都會調用這個completed。

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

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

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

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

派發組(Dispatch Group)

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

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

***個便是dispatch_group_wait,這是一個在組內所有任務執行完畢前或在處理超時的情況下限制當前線程運行的函數。在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將整個方法調至后臺隊列以保證主線程的正常運行。

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

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

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

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

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

條件允許的情況下執行completion閉包。

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

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

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

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

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

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

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

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

派發組,再來一次!

出于精益求精的目的,通過異步的方式將下載任務派發到另一個隊列并用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閉包。當派發組中不再剩余任何任務且輪到completion閉包運行時,這段代碼將會執行。你也可以定義你的completion代碼在哪個隊列上運行。在這段代碼中你便要運行在主隊列上。

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

過多使用并發機制造成的危險

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

 

[[138052]]

Do It !!!

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

該輪到dispatch_apply上場了。

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

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

什么時候適合用dispatch_apply呢?

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

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

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

回到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) 

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

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

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

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

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

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

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

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

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

取消派發塊(dispatch block)的執行

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

咱們可以通過‘利用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。

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

除去前三次的下載,在剩余的數組元素中執行for循環。

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

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

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

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

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

GCD帶來的各種各樣的樂趣

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

測試異步代碼

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

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

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

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

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

信號量(Semaphores)

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

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

信號量適用于讓你在資源有限的情況下控制多個單位的資源消耗。舉個例子,假如你聲明了一個其中包含兩個資源的信號量,在同一時間內最多只能有兩個線程訪問臨界區。其他想使用資源的線程必須以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"

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

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

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

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

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

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

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

期望(Expectations)

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

用以下代碼代替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中顯示這段字符串參數,以此代表著你所期待發生的事情。

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

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

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

派發源(Dispatch Sources)的使用

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

很多派發源的初學者經常被卡在如何使用一個源的的問題上,所以你要清楚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的用法與解釋。

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

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

打開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對派發源進行單次的初始化設定。

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

假如你提供了一個錯誤的參數,派發源對象將不會被創建成功。總之,在使用派發源對象前你要確定你已經創建了一個可用的派發源對象。

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

在默認的情況下,所有派發源都是在暫停掛起的狀態下開始執行的。當你打算開始監管事件時,你必須讓派發源對象從新開始運行。

編譯并運行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中對象的堆棧跟蹤器。

 

[[138053]]

What ?

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

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

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

 

[[138054]]

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

Where to Go From Here?

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

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

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

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

祝你好運!

責任編輯:chenqingxiang 來源: EthanJoe的簡書
相關推薦

2013-07-15 16:18:08

2015-01-26 09:57:47

GradleMaven Centr

2012-09-20 10:50:34

IBMdw

2011-08-15 11:13:06

IOS開發并發Dispatch Qu

2020-09-30 10:11:34

漏洞

2013-07-15 15:51:32

iOS多線程GCD基本概念Dispatch Qu

2024-06-06 10:13:04

2021-09-09 05:22:47

云資訊云計算Grand View

2009-06-14 18:29:14

ibmdwWebSphere

2025-04-14 11:43:26

2024-04-09 10:39:43

安全生成式AI

2010-08-31 16:23:10

DB2Quest Centr管理

2015-01-06 15:02:40

CES2015中興Grand X Max

2021-06-01 07:55:43

Vuex使用流程

2021-04-25 09:18:33

網絡安全服務市場漏洞

2025-10-10 14:27:43

2025-03-05 00:00:00

ReactstoreUI 更新

2025-03-05 00:00:00

state變更組件

2021-04-08 06:02:10

人工智能AI機器學習

2024-07-01 08:27:05

KeyAndroid按鍵事件
點贊
收藏

51CTO技術棧公眾號

91福利视频网站| 欧美午夜精彩| 亚洲成人中文在线| 蜜桃精品久久久久久久免费影院| 999视频在线| 国产欧美日韩电影| 亚洲18女电影在线观看| 日本不卡免费新一二三区| 在线观看国产精品入口男同| 国产精品magnet| 亚洲午夜精品视频| 人妻少妇偷人精品久久久任期| 日韩av影片| 亚洲欧美影音先锋| 精品一区二区三区视频日产| 玖玖爱免费视频| 少妇精品视频在线观看| 亚洲一区二区欧美| 亚洲欧洲免费无码| 日本精品一二区| 蜜臀va亚洲va欧美va天堂| 欧美激情啊啊啊| 粉嫩精品久久99综合一区| 8x国产一区二区三区精品推荐| 日本久久一区二区| 亚洲欧洲在线观看| 免费观看精品视频| a黄色片在线观看| 久久综合一区二区| 91免费的视频在线播放| 久久久久久久久黄色| 午夜精品免费| 色综合网站在线| 日本特级黄色大片| 岛国在线视频| 日本不卡视频一二三区| 欧美激情视频在线免费观看 欧美视频免费一 | 亚洲iv一区二区三区| 无码任你躁久久久久久久| 狠狠综合久久av一区二区老牛| 日韩在线国产精品| 国产免费一区二区三区网站免费| 久草在线综合| 精品少妇一区二区三区在线视频| 中文字幕成人免费视频| 欧美三区四区| 国产精品全国免费观看高清| 你懂的视频在线一区二区| 亚洲黄色在线观看视频| 国产一区二区三区美女| 成人免费看片视频| ,一级淫片a看免费| 久久精品免费观看| 欧美另类高清videos| 永久免费看片视频教学| 成人国产精品一级毛片视频| 亚洲欧美一区二区三区四区| 中文字幕在线观看网址| 69堂精品视频在线播放| 欧美性xxxx在线播放| 91专区在线观看| 中文字幕人成乱码在线观看| 欧美日韩黄色大片| 久草资源站在线观看| 国产三级视频在线看| 91女神在线视频| 欧美人与性禽动交精品| 国产视频福利在线| 国产精品久久久久久久久免费相片| 欧洲在线视频一区| av网站在线播放| 国产精品羞羞答答xxdd| 欧美亚洲国产日韩2020| 久久久久久久久影院| 久久不射2019中文字幕| 国产脚交av在线一区二区| 秋霞av一区二区三区| 日韩电影在线一区| 成人中文字幕+乱码+中文字幕| 国产美女免费看| 粉嫩蜜臀av国产精品网站| 久久99九九| 国产私拍精品| 亚洲乱码日产精品bd| 久久精品中文字幕一区二区三区| 五月激情婷婷网| 久久亚洲春色中文字幕久久久| 日本高清久久一区二区三区| 在线日本中文字幕| 一区二区三区四区高清精品免费观看| 99国产精品白浆在线观看免费| 国产精品25p| 色噜噜偷拍精品综合在线| 亚洲欧美aaa| 狠狠一区二区三区| 在线播放日韩专区| 久久久久久天堂| 日日夜夜精品免费视频| 亚洲xxxxx电影| 日本福利在线观看| 亚洲色图19p| 久久久久久久久久久久久国产精品 | 极品尤物一区| 中文字幕日韩有码| 麻豆国产精品一区| 亚欧美无遮挡hd高清在线视频| 色综合久久中文字幕综合网小说| 亚洲天堂一区在线观看| 激情欧美一区二区三区在线观看| 国产精品一级久久久| 国产福利资源在线| 久久精品一区二区三区av| 久草精品电影| 黄色免费网站在线观看| 欧美日韩一区二区在线播放| 99999精品| 精品久久电影| 2018国产精品视频| www三级免费| 国产精品久久久久久一区二区三区| 99热亚洲精品| 9.1麻豆精品| 国产一区二区三区网站| 日本中文字幕在线免费观看| 狠狠色丁香久久婷婷综合丁香| 你懂的视频在线一区二区| 欧美黑人xx片| 91麻豆精品国产无毒不卡在线观看 | 8av国产精品爽爽ⅴa在线观看 | 日韩在线播放视频| 亚洲欧洲国产精品久久| 草草在线视频| 日韩一区二区在线看片| 色撸撸在线视频| 成人另类视频| 米奇精品一区二区三区在线观看| 欧美成人黑人| 亚洲欧洲av另类| 国产素人在线观看| 亚洲一二av| 久久久av一区| 中文字幕av资源| 久久久噜噜噜久久人人看| 国产精品自拍片| 凹凸成人在线| 欧美激情手机在线视频| 国产肥老妇视频| **欧美大码日韩| 免费黄色特级片| 免费av在线播放| 欧美性受xxxx黑人xyx性爽| 国产激情视频网站| 在线播放精品| 午夜精品久久久久久久99热| 国产免费av观看| 亚洲日本一区二区| 日本高清免费在线视频| 91精品国产福利在线观看麻豆| 国产精品视频自在线| 亚洲日本无吗高清不卡| 人妻视频一区二区三区| 亚洲成人www| 国产麻豆xxxvideo实拍| 亚洲一区视频| 天堂√在线观看一区二区| 成人黄色免费观看| 久久视频免费观看| aaa级黄色片| 亚洲福中文字幕伊人影院| 亚洲少妇18p| 欧美专区一区二区三区| 亚洲精品在线观看免费| 国产美女视频一区二区| 欧美高清视频一区二区| 欧美一区二不卡视频| 精品美女国产在线| 国产传媒在线看| 精久久久久久久久久久| 日韩精品一区二区在线视频| 日韩电影不卡一区| 在线成人动漫av| 日韩女优在线播放| av网站在线免费播放| 91精品免费观看| 国产真实乱人偷精品视频| 91麻豆精品视频| 亚洲欧美日韩综合网| 北岛玲精品视频在线观看| 久久中文字幕一区| 少妇一级淫片免费看| 日本高清不卡aⅴ免费网站| 99热99这里只有精品| 成人在线一区二区三区| www.欧美日本| 巨人精品**| 国产精品久久久久久亚洲调教| 精品视频在线一区二区| 亚洲国产另类 国产精品国产免费| 亚洲熟女综合色一区二区三区| 国产精品美女久久久久aⅴ | 日韩免费高清av| 国产精品免费精品一区| 日韩一区中文字幕| 日韩片在线观看| 国内精品国产三级国产a久久 | 男人av在线播放| 中文字幕日韩在线观看| 色丁香婷婷综合久久| 欧美日韩成人在线一区| 国产精品第9页| 亚洲欧美日韩国产成人精品影院| 黄色a一级视频| 国产精品一区一区三区| 黄色一级二级三级| 五月婷婷六月综合| 日本精品一区二区三区高清 久久| 国产色99精品9i| 国产精品777| 69av成人| 精品中文字幕乱| 在线观看美女网站大全免费| 日韩av在线高清| 亚洲国产www| 91超碰这里只有精品国产| 欧美人一级淫片a免费播放| 成av人片一区二区| 爱情岛论坛亚洲自拍| 蓝色福利精品导航| 久热免费在线观看| 国产精品日韩欧美一区| 久久久久99精品成人片| 影视亚洲一区二区三区| 亚洲精品视频一二三| 真实原创一区二区影院| 精品久久久久久乱码天堂| 欧美 日韩 国产 精品| 日韩大陆欧美高清视频区| 97久久久久久久| 亚洲综合在线免费观看| 人与动物性xxxx| 欧美国产一区二区| www.激情网| 久久婷婷蜜乳一本欲蜜臀| 日本一区二区在线视频观看| 色吊丝一区二区| 国产九色精品| 都市激情亚洲欧美| 45www国产精品网站| 欧美日韩经典丝袜| 色综合视频网站| 青春草视频在线| 欧美老女人性视频| 色爱综合区网| 欧美激情视频播放| heyzo高清国产精品| 久久久亚洲网站| 成人区精品一区二区不卡| 日韩视频第一页| 成人福利在线观看视频| 久久躁狠狠躁夜夜爽| 最新av在线播放| 久久久久日韩精品久久久男男| 国产精品一区hongkong| 992tv成人免费影院| 2022成人影院| 国产精品盗摄久久久| 成人黄页网站视频| 91探花福利精品国产自产在线| 久久的色偷偷| 国产一级特黄a大片99| 卡通动漫国产精品| 日韩欧美三级电影| 天天综合网网欲色| 免费的av在线| 亚洲国产mv| 美女黄色片视频| 国产一区二区h| 丝袜熟女一区二区三区| 久久综合九色综合久久久精品综合 | 欧美老女人在线| 黄色一级视频在线观看| 亚洲国产人成综合网站| 九一国产在线观看| 欧美视频在线不卡| 亚洲精品18在线观看| 亚洲精品久久久久久久久| 搞黄视频免费在线观看| 久久久国产精品亚洲一区| av中文在线资源| 国产精品第一页在线| 精品中文字幕一区二区三区| 久久国产精品久久| 天天精品视频| 777久久久精品一区二区三区 | 久久精品亚洲人成影院| 国产成人一区二区三区别| 久久成人精品| 极品粉嫩美女露脸啪啪| 不卡高清视频专区| 激情五月激情综合| 精品久久久久久久久久国产| 少妇又紧又色又爽又刺激视频 | 午夜av一区| 欧美日韩在线中文| 国产精品亚洲午夜一区二区三区 | 97热在线精品视频在线观看| 精品欧美日韩精品| 国产美女在线精品免费观看| 日韩欧美网址| 欧美在线观看成人| 国产超碰在线一区| 国产又粗又猛又爽又黄的视频小说| 亚洲一区日韩精品中文字幕| 国产精品无码粉嫩小泬| 亚洲电影av在线| xvideos国产在线视频| 国产91久久婷婷一区二区| 99这里只有精品视频| 一区二区三区四区不卡| 性高湖久久久久久久久| 中文写幕一区二区三区免费观成熟| 亚洲国产高清aⅴ视频| 日本视频免费在线| 欧美成人aa大片| 老司机精品视频在线观看6| 国产精品18久久久久久首页狼| 欧美日韩一区二区三区四区不卡| 国产精品无码乱伦| 美国十次了思思久久精品导航| 亚洲国产综合视频| 亚洲一二三区在线观看| 国产黄色一级大片| www.亚洲免费视频| 欧美videos粗暴| 日韩欧美一区二区在线观看| 性一交一乱一区二区洋洋av| 国产xxxx视频| 亚洲h精品动漫在线观看| 国产福利视频导航| 美女福利视频一区| 99国内精品久久久久| 亚洲综合av一区| 久久狠狠亚洲综合| 欧美性生给视频| 欧美日韩精品欧美日韩精品一综合| 国产视频福利在线| 国产精品亚洲综合天堂夜夜| japanese国产精品| 一区二区三区网址| 国产精品二三区| 国产免费叼嘿网站免费| 九九热这里只有精品6| 免费看日产一区二区三区| 欧美激情亚洲天堂| 99综合电影在线视频| 91精品人妻一区二区三区蜜桃欧美 | 亚洲精品国精品久久99热一| 男人的天堂免费在线视频| 蜜桃999成人看片在线观看| 丝袜美腿亚洲综合| 青青青视频在线免费观看| 91精品免费在线| 丁香花在线高清完整版视频| 精品无人乱码一区二区三区的优势| 国产亚洲在线观看| av手机在线播放| 欧美精品视频www在线观看| 色yeye免费人成网站在线观看| 国产视频不卡| 日韩和欧美一区二区| 欧美性生交大片| 欧美xxxxx牲另类人与| 无遮挡爽大片在线观看视频 | 欧美日本国产| 中文字幕一区二区久久人妻网站 | 日本人妻丰满熟妇久久久久久| 欧美亚洲一区在线| 欧美激情偷拍自拍| 97人妻精品一区二区三区免费 | 亚洲黄色a v| 中文字幕人成不卡一区| 成人免费观看在线视频| 最近中文字幕日韩精品| 日韩中文字幕无砖| 男女午夜激情视频| 国产精品卡一卡二| 人妻无码中文字幕| 国产成人综合精品在线| 亚洲色图88| 亚洲天堂av网站| 在线观看日韩毛片| h片在线观看网站| 蜜桃视频成人| 美女脱光内衣内裤视频久久网站 | 欧美一区二区黄片| 日本最新高清不卡中文字幕| 我不卡伦不卡影院| 中文字幕人妻无码系列第三区| 午夜久久久久久久久 | 中文字幕在线成人|