1.理解"塊"這一概念
- 塊是 C,C++,Object-C 中的語(yǔ)法閉包.
- 塊可以接收參數(shù),也可以返回參數(shù).
- 如果塊里捕獲的是對(duì)象類型的變量,那么塊就會(huì)自動(dòng)保存它,系統(tǒng)在釋放這個(gè)塊的時(shí)候,也會(huì)將其一并釋放.
- 因?yàn)槊總€(gè)實(shí)例變量的個(gè)數(shù)及對(duì)象所包含的關(guān)聯(lián)數(shù)據(jù)互不相同,所以每個(gè)對(duì)象所占內(nèi)存區(qū)域也有大有小.
- 在塊里面捕獲所持有的對(duì)象,拷貝的并不是對(duì)象本身,而是指向?qū)ο蟮闹羔樧兞?
- 除了"棧塊"和"堆塊"之外,還有一種叫"全局塊(gloab block)".全局塊的拷貝操作是個(gè)空的操作,因?yàn)槿謮K決不可能為系統(tǒng)所回收.實(shí)際上相當(dāng)于單利.
- 塊可以分配在堆或棧上,也可以是全局的.分配在棧上的塊可拷貝到堆里,這樣的話,就和標(biāo)準(zhǔn)的 Object-C 對(duì)象一樣,具備引用計(jì)數(shù)了.
2.為常用的塊類型創(chuàng)建 typedef
- 以 typedef 重新定義塊類型,可令塊變量用起來(lái)更加簡(jiǎn)單.
- 定義新類型時(shí),必須遵從現(xiàn)有的,命名習(xí)慣,勿使其名稱與別的類型相沖突.
- 每個(gè)塊都具備其"固定類型(inherent type)",因而可將其賦值給適當(dāng)類型的變量.
- 使用類型定義后,當(dāng)你打算重構(gòu)塊的類型簽名時(shí)會(huì)很方便.
- 最好在使用塊類型的類中定義這些 typedef, 而且還應(yīng)該把這個(gè)類的名字加在由 typedef 所定義的新類型名前面,這樣就可以闡明塊的用途.
- 不妨為同一個(gè)塊簽名定義多個(gè)類型別名.如果要重構(gòu)的代碼使用了塊類型的某個(gè)別名,那么只需修改相應(yīng) typedef 中的塊簽名,無(wú)須改動(dòng)其他 typedef.
3.用 hander 塊降低代碼分散程度
- 在創(chuàng)建對(duì)象時(shí),可以使用內(nèi)聯(lián)的 hander 塊將相關(guān)業(yè)務(wù)邏輯一并聲明.
- 在有多個(gè)實(shí)例要監(jiān)控時(shí),如果采用委托模式,那么經(jīng)常需要根據(jù)傳入的對(duì)像來(lái)切換,而若改用 hander 塊來(lái)實(shí)現(xiàn),則可直接將塊與相關(guān)對(duì)象放在一起.
- 設(shè)計(jì) API 時(shí)如果用到了 hander 塊,那么可以增加一個(gè)參數(shù),使調(diào)用者可通過(guò)此參數(shù)來(lái)決定應(yīng)該吧塊安排在哪個(gè)隊(duì)列上執(zhí)行.
4.用塊引用其所屬對(duì)象時(shí)不要出現(xiàn)保留環(huán)
- 如果塊所捕獲的對(duì)象直接或間接的保留了塊本身,那么就得當(dāng)心保留環(huán)的問(wèn)題.
- 一定要找個(gè)適當(dāng)?shù)臅r(shí)機(jī)解除保留環(huán),而不能把責(zé)任退給 API 的調(diào)用者.
4.多用派發(fā)隊(duì)列,少用同步鎖
- @synchronized 屬性原子性內(nèi)部實(shí)現(xiàn)鎖,可以提供某種程度的"線程安全(therad safety)",但卻無(wú)法保證訪問(wèn)該對(duì)象時(shí)絕對(duì)是線程安全.
- 簡(jiǎn)單而高效的代替同步塊或鎖對(duì)象的方法,就是使用"串行同步隊(duì)列(serial synchoronization queue)".
- 在并發(fā)隊(duì)列中,柵欄塊必須單獨(dú)執(zhí)行,不能與其他塊并行.這支隊(duì)并發(fā)隊(duì)列有意義,因?yàn)榇嘘?duì)列中的塊總是按順序逐個(gè)來(lái)執(zhí)行的.
- 派發(fā)隊(duì)列可用來(lái)表示同步語(yǔ)義(synchroniztion semantic),這種做法要比使用@ synchronized 塊或 NSLock 對(duì)象更簡(jiǎn)單.
- 將同步與異步派發(fā)結(jié)合起來(lái),可以實(shí)現(xiàn)與普通加鎖機(jī)制一樣的同步操作,而這么做卻不會(huì)阻塞執(zhí)行異步派發(fā)線程.
- 使用同步隊(duì)列及柵欄塊,可以令同步行為更加高效.
5.多用 GCD, 少用 performSelector 方法
- 用 performSelector:系類方法,編譯器并不知道將要調(diào)用的選擇子是什么,因此,也就不了解其方法的簽名及返回值,甚至連是否有返回值都不清楚.而且,由于編譯器不知道方法名,所以就沒(méi)辦法運(yùn)用 ARC 的內(nèi)存管理規(guī)則來(lái)判定返回值是不是應(yīng)該釋放.鑒于此, ARC 采用了比較謹(jǐn)慎的做法,就是不加添釋放操作.然而這么做很可能導(dǎo)致內(nèi)存泄露,因?yàn)榉椒ㄔ诜祷貙?duì)象時(shí)有可能已經(jīng)將其保留了.
- performSelector系類方法所能處理的選擇子太過(guò)局限了,選擇子的返回值類型及發(fā)送給方法的參數(shù)個(gè)數(shù)都受限制.
- 如果想把任務(wù)放到另一個(gè)線程上執(zhí)行,那么最好不要使用 performSeletor 系列方法,而是應(yīng)該把任務(wù)封裝到塊面,然后調(diào)用大中樞派發(fā)機(jī)制的相關(guān)方法來(lái)實(shí)現(xiàn).
6.掌握 GCD 及操作隊(duì)列的使用時(shí)機(jī)
GCD 是純 C 的 API, 而操作隊(duì)列是 Object-C 的對(duì)象.咋 GCD 中,任務(wù)用塊來(lái)表示,而快是輕量級(jí)的數(shù)據(jù)結(jié)構(gòu).與之相反,"操作(operation)"則是更為重量級(jí)的 Object-C 的對(duì)象.
-
使用 NSOperationQueue 及 NSOperation 的好處:
a.取消某個(gè)操作,在任務(wù)執(zhí)行之前,可以在 NSOperation 對(duì)象上調(diào)用 cancel 方法.不過(guò),已經(jīng)啟動(dòng)的任務(wù)無(wú)法取消.
b.制定操作間的依賴關(guān)系.一個(gè)操作可以依賴其它多個(gè)操作.
c. 通過(guò)鍵值觀察機(jī)制監(jiān)控 NSOperation 對(duì)象的屬性.例:isCancelled 屬性來(lái)判斷任務(wù)是否取消. isFinished 屬性判斷任務(wù)是否完成.
d. 制定操作的優(yōu)先級(jí).
e. 重用 NSOperation 對(duì)像.
在解決多線程與任務(wù)管理問(wèn)題時(shí),派發(fā)隊(duì)列并非唯一的方案.
操作隊(duì)列提供了一套高層的 Object-C API,能實(shí)現(xiàn)純 GCD 所具備的大部分功能,而且還能完成一些更為復(fù)雜的操作,那些操作若改用 GCD 來(lái)實(shí)現(xiàn),則需另外編寫代碼.
7.通過(guò) Dispath Group 機(jī)制,根據(jù)系統(tǒng)資源狀態(tài)來(lái)執(zhí)行任務(wù)
- dispath_apply 所用的隊(duì)列可以是并發(fā)隊(duì)列或者串行隊(duì)列. disapath_apply 會(huì)阻塞線程,直到所有的任務(wù)都執(zhí)行完畢為止.
- 假如把塊派發(fā)給了當(dāng)前隊(duì)列(或者體系中高于當(dāng)前隊(duì)列的某個(gè)串行隊(duì)列),就會(huì)導(dǎo)致死鎖.若想在后臺(tái)執(zhí)行任務(wù),則應(yīng)使用 dispath group.
- 一系列任務(wù)可歸入一個(gè) dispath group 之中.開發(fā)者可以在這組任務(wù)執(zhí)行完畢時(shí)獲取通知.
- 通過(guò) dispath group,可以在并發(fā)式派發(fā)隊(duì)列里同時(shí)執(zhí)行多項(xiàng)任務(wù).此時(shí) GCD 會(huì)根據(jù)系統(tǒng)的資源來(lái)調(diào)度這些并發(fā)的任務(wù).開發(fā)者若自己來(lái)實(shí)現(xiàn)此功能,則需編寫大量代碼.
8.使用 dispath_once 來(lái)執(zhí)行只需運(yùn)行一次的線程安全代碼.
- 經(jīng)常需要編寫"只需要執(zhí)行一次的線程安全代碼(thread-safe single-code execution)".通過(guò) GCD 所提供的 dispath_once 函數(shù),很容易就能實(shí)現(xiàn)此功能.
- 標(biāo)記應(yīng)該聲明在 staic 或者 global 作用域中,這樣的話,在把只要執(zhí)行一次的塊傳給 dispath_once 函數(shù)時(shí),傳進(jìn)去的標(biāo)示也是相同的.
9.不要使用 dispath_get_current_queue
- dispath__get_ _ current_queue 在iOS6.0版本起,已經(jīng)正式廢棄使用了,不過(guò) macOs 系統(tǒng)在10.8版本也尚未廢棄.雖然說(shuō)如此,但是在 macOs 系統(tǒng)還是要避免使用它.
- dispath__get_ _ current_queue 函數(shù)行為常常與開發(fā)者所預(yù)期的不同,此函數(shù)廢棄,只應(yīng)用作調(diào)試.
- 由于派發(fā)隊(duì)列是按層級(jí)來(lái)組織的,所以無(wú)法但用某個(gè)隊(duì)列對(duì)象來(lái)描述"當(dāng)前隊(duì)列"這一概念.
- dispath__get_ _ current_queue 函數(shù)用于解決由不可重入的代碼所引發(fā)的死鎖,然而能用此函數(shù)解決的問(wèn)題,通常也能改用"隊(duì)列特定數(shù)據(jù)"來(lái)解決.
- dispath__queue_ _ set_specific "隊(duì)列特定數(shù)據(jù)",用來(lái)給相應(yīng)隊(duì)列添加信息.
void dispath_queue_set_specific(dispath_queue_t queue,
const void *key,
void *context,
dispath_funcation_t destructor);
queue:設(shè)置信息的隊(duì)列;
key:函數(shù)就是按照這個(gè) key 來(lái)比較鍵的.
context:對(duì)隊(duì)列特定數(shù)據(jù)的關(guān)聯(lián)引用,這個(gè)對(duì)象的內(nèi)存管理要自己處理;
destructor:解析構(gòu)造函數(shù),對(duì)于給定的鍵來(lái)說(shuō),當(dāng)隊(duì)列所占內(nèi)存為系統(tǒng)所回收,或者有新的值與鍵相關(guān)聯(lián)時(shí),原有的值對(duì)象就會(huì)被移除,而析構(gòu)函數(shù)也會(huì)此時(shí)運(yùn)行.dispath__funcation _t 類型的定義如下:
typedef void (*dispath_funcation_t)(void *)
由此可見,析構(gòu)函數(shù)只能帶一個(gè)指針參數(shù)且返回值為 void.