Objective-C高階程式設計讀書筆記之GCD
iOS與OS X多執行緒和記憶體管理
Grand Central Dispatch (GCD)
目錄
- 什麼是GCD
- 什麼是多執行緒, 併發
- GCD的優勢
- GCD的API介紹
- GCD的注意點
- GCD的使用場景
- Dispatch Source
- 總結
1. 什麼是GCD
GCD, Grand Central Dispatch, 可譯為”強大的中樞排程器”, 基於libdispatch, 純C語言, 裡面包含了許多多執行緒相關非常強大的函式. 程式設計師可以既不寫一句執行緒管理的程式碼又能很好地使用多執行緒執行任務.
GCD中有Dispatch Queue
Dispatch Queue
蘋果官方對GCD的說明如下 :
開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中.
這句話用源程式碼表示如下
Objective-C12345 | dispatch_async(queue,^{/* * 想執行的任務 */}); |
該原始碼用block的語法定義想執行的任務然後通過dispatch_async函式講任務追加到賦值在變數queue的”Dispatch Queue”中.
Dispatch Queue究竟是什麼???
Dispatch Queue是執行處理的等待佇列, 按照先進先出(FIFO, First-In-First-Out)的順序進行任務處理.
First-In-First-Out另外, 佇列分兩種, 一種是序列佇列(Serial Dispatch Queue), 一種是並行佇列(Concurrent Dispatch Queue).
Dispatch Queue的種類 | 說明 |
---|---|
Serial Dispatch Queue | 等待現在執行中處理結束 |
Concurrent Dispatch Queue | 不等待現在執行中處理結束 |
序列佇列 : 讓任務一個接一個執行
並行佇列併發佇列 : 讓多個任務同時執行(自動開啟多個執行緒執行任務)
併發功能只有在非同步函式(dispatch_async)下才有效(想想看為什麼?)
GCD的API會在下面詳細說明~
2. 什麼是多執行緒, 併發
我們知道, 一個應用就相當於一個程序, 而一個程序可以同時分發幾個執行緒同時處理任務.而併發正是一個程序開啟多個執行緒同時執行任務的意思, 主執行緒專門用來重新整理UI,處理觸控事件等 而子執行緒呢, 則用來執行耗時的操作, 例如訪問資料庫, 下載資料等..
以前我們CPU還是單核的時候, 並不存在真正的執行緒並行, 因為我們只有一個核, 一次只能處理一個任務. 所以當時我們計算機是通過分時也就是CPU地在各個程序之間快速切換, 給人一種能同時處理多工的錯覺
來實現的, 而現在多核CPU計算機則能真真正正貨真價實地辦到同時處理多個任務.
3. GCD的優勢
說到優勢, 當然有比較, 才能顯得出優勢所在. 事實上, iOS中我們能使用的多執行緒管理技術有
- pthread
- NSThread
- GCD
- NSOperationQueue
pthread
來自Clang, 純C語言, 需要手動建立執行緒, 銷燬執行緒, 手動進行執行緒管理. 而且程式碼極其噁心, 我保證你寫一次不想寫第二次…不好意思我先去吐會T~T
NSThread :
Foundation框架下的OC物件, 依舊需要自己進行執行緒管理,執行緒同步。 執行緒同步對資料的加鎖會有一定的開銷。
GCD :
兩個字, 牛逼, 雖然是純C語言, 但是它用難以置信的非常簡潔的方式實現了極其複雜的多執行緒程式設計, 而且還支援block內聯形式進行制定任務. 簡潔! 高效! 而且我們再也不用手動進行執行緒管理了.
NSOperationQueue :
相當於Foundation框架的GCD, 以面向物件的語法對GCD進行了封裝. 效率一樣高.
GCD優勢在哪裡?
- GCD會自動利用更多的CPU核心
- GCD會自動管理執行緒的生命週期
- 使用方法及其簡單
怎麼樣? 心動不, 迫不及待想要知道怎麼使用GCD了吧, 那我們馬上切入正題~
4. GCD的API介紹
在介紹GCD的API之前, 我們先搞清楚四個名詞: 序列, 並行, 同步, 非同步
- 序列 : 一個任務執行完, 再執行下一個任務
- 並行 : 多個任務同時執行
- 同步 : 在當前執行緒中執行任務, 不具備開啟執行緒的能力
- 非同步 : 在新的執行緒中執行任務, 具備開啟執行緒的能力
下面開始介紹GCD的API
建立佇列
Objective-C1 | dispatch_queue_create(constchar*label,dispatch_queue_attr_t attr) |
手動建立一個佇列.
- label : 佇列的識別符號, 日後可用來除錯程式
- attr : 佇列型別
DISPATCH_QUEUE_CONCURRENT : 併發佇列
DISPATCH_QUEUE_SERIAL 或 NULL : 序列佇列
需要注意的是, 通過dispatch_queue_create函式生成的queue在使用結束後需要通過dispatch_release函式來釋放.(只有在MRC下才需要釋放)
並不是什麼時候都需要手動建立佇列, 事實上系統給我們提供2個很常用的佇列.
主佇列
Objective-C1 | dispatch_get_main_queue(); |
該方法返回的是主執行緒中執行的同步佇列. 使用者介面的更新等一些必須在主執行緒中執行的操作追加到此佇列中.
全域性併發佇列
Objective-C1 | dispatch_get_global_queue(longidentifier,unsignedlongflags); |
該方法返回的是全域性併發佇列. 使用十分廣泛.
- identifier : 優先順序
DISPATCH_QUEUE_PRIORITY_HIGH : 高優先順序
DISPATCH_QUEUE_PRIORITY_DEFAULT : 預設優先順序
DISPATCH_QUEUE_PRIORITY_LOW : 低優先順序
DISPATCH_QUEUE_PRIORITY_BACKGROUND : 後臺優先順序 - flags : 暫時用不上, 傳 0 即可
注意 : 對Main Dispatch Queue和Global Dispatch Queue執行dispatch_release和dispatch_retain沒有任何問題. (MRC)
同步函式
Objective-C1 | dispatch_sync(dispatch_queue_t queue,^(void)block); |
在引數queue佇列下同步執行block
非同步函式
Objective-C1 | dispatch_async(dispatch_queue_t queue,^(void)block); |
在引數queue佇列下非同步執行block(開啟新執行緒)
時間
Objective-C1 | dispatch_time(dispatch_time_t when,int64_t delta); |
根據傳入的時間(when)和延遲(delta)計算出一個未來的時間
- when :
DISPATCH_TIME_NOW : 現在
DISPATCH_TIME_FOREVER : 永遠(別傳這個引數, 否則該時間很大) - delta : 該引數接收的是納秒, 可以用一個巨集NSEC_PER_SEC來進行轉換, 例如你要延遲3秒, 則為 3 * NSEC_PER_SEC.
延遲執行
Objective-C1 | dispatch_after(dispatch_time_t when,dispatch_queue_t queue,^(void)block); |
有了上述獲取時間的函式, 則可以直接把時間傳入, 然後定義該延遲執行的block在哪一個queue佇列中執行.
蘋果還給我們提供了一個在主佇列中延遲執行的程式碼塊, 如下
Objective-C123 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delayInSeconds *NSEC_PER_SEC)),dispatch_get_main_queue(),^{code to be executed afteraspecified delay}); |
我們只需要傳入需要延遲的秒數(delayInSeconds)和執行的任務block就可以直接呼叫了, 方便吧~
注意 : 延遲執行不是在指定時間後執行任務處理, 而是在指定時間後將處理追加到佇列中, 這個是要分清楚的
佇列組
Objective-C1 | dispatch_group_create(); |
有時候我們想要在佇列中的多個任務都處理完畢之後做一些事情, 就能用到這個Group. 同隊列一樣, Group在使用完畢也是需要dispatch_release掉的(MRC). 上程式碼
group組非同步函式
Objective-C1 | dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,^(void)block); |
分發Group內的併發非同步函式
組通知
Objective-C1 | dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,^(void)block) |
監聽group的任務進度, 當group內的任務全部完成, 則在queue佇列中執行block.
組等待
Objective-C1 | dispatch_group_wait(dispatch_group_t group,dispatch_time_t timeout) |
- timeout : 等待的時間
DISPATCH_TIME_NOW : 現在
DISPATCH_TIME_FOREVER : 永遠
該函式會一直等待組內的非同步函式任務全部執行完畢才會返回. 所以該函式會卡住當前執行緒. 若引數timeout為DISPATCH_TIME_FOREVER, 則只要group內的任務尚未執行結束, 就會一直等待, 中途不能取消.
柵欄
Objective-C1 | dispatch_barrier_async(dispatch_queue_t queue,^(void)block) |
在訪問資料庫或檔案時, 為了提高效率, 讀取操作放在並行佇列中執行. 但是寫入操作必須在序列佇列中執行(避免資源搶奪問題). 為了避免麻煩, 此時dispatch_barrier_async函式作用就出來了, 在這函式裡進行寫入操作, 寫入操作會等到所有讀取操作完畢後, 形成一道柵欄, 然後進行寫入操作, 寫入完畢後再把柵欄移除, 同時開放讀取操作. 如圖
dispatch_barrier_async快速迭代
Objective-C123 | dispatch_apply(10,dispatch_get_global_queue(0,0),^(size_t index){// code here}); |
執行10次程式碼, index順序不確定. dispatch_apply會等待全部處理執行結束才會返回. 意味著dispatch_apply會阻塞當前執行緒. 所以dispatch_apply一般用於非同步函式的block中.
一次性程式碼
Objective-C1234 | staticdispatch_once_t onceToken;dispatch_once(&onceToken,^{// 只執行1次的程式碼(這裡面預設是執行緒安全的)}); |
該程式碼在整個程式的生命週期中只會執行一次.
掛起和恢復
Objective-C1 | dispatch_suspend(queue) |
掛起指定的queue佇列, 對已經執行的沒有影響, 追加到佇列中尚未執行的停止執行.
Objective-C1 | dispatch_resume(queue) |
恢復指定的queue佇列, 使尚未執行的處理繼續執行.
5. GCD的注意點
因為在ARC下, 不需要我們釋放自己建立的佇列, 所以GCD的注意點就剩下死鎖
死鎖
Objective-C12345 | NSLog(@"111");dispatch_sync(dispatch_get_main_queue(),^{NSLog(@"222");});NSLog(@"333"); |
以上三行程式碼將輸出什麼?
111
222
333
?
還是
111
333
?
其實都不對, 輸出結果是
111
為什麼? 看下圖
死鎖毫無疑問會先輸出111, 然後在當前佇列下呼叫dispatch_sync函式, dispatch_sync函式會把block追加到當前佇列上, 然後等待block呼叫完畢該函式才會返回, 不巧的是, block在佇列的尾端, 而佇列正在執行的是dispatch_sync函式. 現在的情況是, block不執行完畢, dispatch_sync函式就不能返回, dispatch_sync不返回, 就沒機會執行block函式. 這種你等我, 我也等你的情況就是死鎖, 後果就是大家都執行不了, 當前執行緒卡死在這裡.
如何避免死鎖?
不要在當前佇列使用同步函式, 在佇列巢狀的情況下也不允許. 如下圖,
佇列巢狀呼叫同步函式引發死鎖大家可以想象, 佇列1執行完NSLog後到佇列2中執行NSLog, 佇列2執行完後又跳回佇列1中執行NSLog, 由於都是同步函式, 所以最內層的NSLog(“333”); 追加到佇列1中, 實際上最外層的dispatch_sync是還沒返回的, 所以它沒有執行的機會. 也形成死鎖. 執行程式, 果不其然, 列印如下 :
111
222
6. GCD的使用場景
執行緒間的通訊
這是GCD最常用的使用場景了, 如下程式碼
Objective-C123456 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{// 執行耗時操作dispatch_async(dispatch_get_main_queue(),^{// 回到主執行緒作重新整理UI等操作});}); |
為了不阻塞主執行緒, 我們總是在後臺執行緒中傳送網路請求, 處理資料, 然後再回到主執行緒中重新整理UI介面
單例
單例也就是在程式的整個生命週期中, 該類有且僅有一個例項物件, 此時為了保證只有一個例項物件, 我們這裡用到了dispatch_once函式
Objective-C12345678910111213141516171819202122232425 | staticXXTool<em>_instance;+(instancetype)allocWithZone:(struct_NSZone</em>)zone{staticdispatch_once_t onceToken;dispatch_once(&onceToken,^{_instance=[self allocWithZone:zone];});return_instance;}相關推薦Objective-C高階程式設計讀書筆記之GCDObjective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 Grand Central Dispatch (GCD) 目錄 什麼是GCD 什麼是多執行緒, 併發 GCD的優勢 GCD的API介紹 GCD的注意點 GCD的使用場景 Dispatch Sou Objective-C高階程式設計讀書筆記之記憶體管理Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 自動引用計數(ARC, Automatic Reference Counting) 目錄 什麼是自動引用計數 記憶體管理的思考方式 autorelease 所有權修飾符介紹 ARC規則 ARC實 Objective-C高階程式設計讀書筆記之blocksObjective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 Blocks 這裡有五道關於block的測試題, 大家可以去做做測試看看自己對block瞭解多少. 目錄 Block的定義 Block有哪幾種類型 Block特性 __block修飾符 bloc JavaScript高階程式設計讀書筆記之JSONJSON(JavaScript Object Notation)JavaScript物件表示法。JSON是JavaScript的一個嚴格的子集,利用了JavaScript中的一些模式來表示結構化資料。關於JSON,最重要的是理解它是一種資料格式,不是一種程式語言。雖然具有相同的語法形式,但JSON並不從屬於J Javascript高階程式設計--讀書筆記之面向物件(一)哈哈哈萬物皆物件,終於到了js的面向物件篇。 一、屬性型別 (1)資料屬性 資料屬性包含一個數據值的位置,在這個位置可以寫入和讀取數值,資料屬性有四個描述器行為的特性 [[Configurable]]:表示能否通過 delete 刪除屬性而重新定義屬性,預設值是ture [[Enumerab Object-C高階程式設計讀書筆記(5)——Block的物件型別擷取在之前的部落格中,我們探討了Block對於普通型別資料的擷取,其實現很簡單,就是在Block物件中儲存一份值拷貝。 那麼,對於OC中的物件型別(包括系統自帶型別NSArray,NSString和自定義物件型別),Block又是怎麼儲存的呢?在《OC高階程式設計》書中對於該部 Object-C高階程式設計讀書筆記(3)——Block的變數擷取之前我們對於Block的定義為 “帶有自動變數值的匿名函式”。通過前面的介紹,知道了Block能夠保持傳入其中的變數的值,即使在Block外部這些傳入的值已經結束了其作用域,但是在Block被呼叫時, 《Objective-C高階程式設計》讀書筆記--2.3.1--Blocks的實質前言 Blocks的原理,每當自己對知識體系有一定提升之後,再回過頭來看一下曾經讀過的書籍,會發現對它的理解逐步加深。藉著讀書筆記活動,立個小目標,把Block徹底搞明白,重讀《Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理》第二章節block原理部分,一方面給自己做個筆記,另一方 讀書筆記:Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 ——(持續)1.記憶體管理的思考方式: .自己生成的物件,自己所持有.非自己生成的物件,自己也能持有不再需要自己持有的物件時釋放非自己持有的物件無法釋放由NSObject類擔任下面管理職責 eg: / Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 讀書筆記(一)1.2.2 記憶體管理原則: 自己生成的物件,自己所持有 非自己生成的物件,自己也能持有 不再需要自己持有的物件時釋放 非自己持有的物件無法釋放 自己生成的物件,自己所持有 //自己生成並持有物件 id obj = [[NSObject alloc] init]; //自己持有物件 block 知識點 ---- Objective-C 高階程式設計 iOS 與 OS X 多執行緒記憶體管理 學習筆記1. block捕捉變數: 結論:只有呼叫_Block_copy 才能持有截獲的附有 __strong 修飾符的物件型別的自動變數值。 block 中使用物件型別的自動變數時,除以下情形,推薦使用copy方法: “When the Block is returned Objective-C高階程式設計:iOS與OS X多執行緒和記憶體管理這篇文章主要給大家講解一下GCD的平時不太常用的API,以及文末會貼出GCD定時器的一個小例子。 需要學習的朋友可以通過網盤免費下載pdf版 (先點選普通下載-----再選擇普通使用者就能免費下載了)http://putpan.com/fs/cy1i1beebn7s0h4u9/ 1.G python高階程式設計讀書筆記(一)python高階程式設計讀書筆記(一) python 高階程式設計讀書筆記,記錄一下基礎和高階用法 python2和python3相容處理 使用sys模組使程式python2和python3相容 import sysver=sys.version_info#(ma JavaScript高階程式設計學習筆記之事件1、事件流 事件流描述的是從頁面中接收事件的順序。 事件冒泡 IE的事件流叫做事件冒泡(event bubbling),即事件開始時由最具體的元素(文件中巢狀層次最深的那個節點)接收,然後逐級向上傳播到較為不具體的節點(文件)。(DOM樹向上傳播)(通俗解釋(個人理解: 當一個元素上的事件被觸發的時候,比如 javascript高階程式設計讀書筆記(1)第 1章 JavaScript 簡介第 1章 JavaScript 簡介 1.1 JavaScript 簡史 1.2 JavaScript 實現 一個完整的 JavaScript 實現應該由下列三 個不同的部分組成(見圖 1-1)。 核心(ECMAScript) 文件物件模型(DOM) JavaScript高階程式設計讀書筆記(二)變數 作用域和記憶體ECMAScript可能包含兩種不同的資料型別,基本資料型別指的是簡單的資料欄位,而引用型別指那些可能由多個指構成的物件。 1、基本資料型別是按值訪問的,因為可以操作儲存在變數中的實際值。 2、引用型別的是按引用訪問的,因為引用型別的值是儲存在記憶體中的物件,ECMAScript不允許直接訪問記憶體中的位 Linux C高階程式設計——檔案操作之庫函式Linux C高階程式設計——檔案操作之庫函式 宗旨:技術的學習是有限的,分享的精神是無限的 ——為什麼要設計標準I/O庫? 直接使用API進行檔案訪問時,需要考慮許多細節問題 例如:read、write時,緩衝區的大小該如何確定,才能使效率最優 標準I/O庫封裝了諸多 Linux C高階程式設計——檔案操作之系統呼叫Linux C高階程式設計檔案操作之系統呼叫 宗旨:技術的學習是有限的,分享的精神是無限的! 庫函式是一些完成特定功能的函式,一般由某個標準組織製作釋出,並形成一定的標準。使用庫函式編寫的函式一般可以應用於不同的平臺而不需要做任何修改,具有很好的可移植 讀《Objective-C高階程式設計iOS與OS X多執行緒和記憶體管理》最近一週,公司在廣州有釋出會。去廣州的人很忙,留在公司的開發人員有時也很“忙”。趁著空閒的時間,看了《Objective-C高階程式設計iOS與OS X多執行緒和記憶體管理》這書,網上找的pdf版本。 這本書分三給部分,依次是ARC、Blocks和GCD。 ARC從非 Unix環境高階程式設計 讀書筆記 第三章 檔案IO概述 對於open、read、write、lseek、close等函式,被稱為不帶緩衝的I/O。即unbuffered i/O,術語“不帶緩衝”指的是每個read或者write都呼叫核心中的一個系統呼叫。 檔案描述符 檔案描述符是一個非負的整數; 檔案描述符0與程序 |