《Objective-C基礎(chǔ)教程》讀書筆記17—代碼塊和并發(fā)性

代碼塊勇劣,一個可以增強函數(shù)功能的Objective-C特性柠衍。你可以在運行著iOS(版本4以上)和OS X(版本10.6以上)的應(yīng)用程序中使用代碼塊再沧。并發(fā)性:如何讓現(xiàn)代設(shè)備同時執(zhí)行多個任務(wù)尺栖。

  1. 代碼塊
    代碼塊對象(簡稱為代碼塊)是對C語言中函數(shù)的擴展。除了函數(shù)中的代碼补疑,代碼塊還包含變量綁定班利。代碼塊有時也被稱為閉包洞豁。
    代碼塊包含兩種類型的綁定: 自動型與托管型。自動綁定使用的是棧中的內(nèi)存茸歧,而托管綁定是通過堆創(chuàng)建的倦炒。
    因為代碼塊底層實際上是由C語言實現(xiàn)的,所以它們在各種以C作為基礎(chǔ)的語言內(nèi)都是有效的软瞎,包括Objective-C逢唤、C++以及Objective-C++。
    代碼塊在Xcode的GCC和Clang工具中是有效的涤浇,但它不屬于ANSI的C語言標準鳖藕。
    1.1 代碼塊和函數(shù)指針
    代碼塊借鑒了函數(shù)指針的語法。所以如果你知道如何聲明函數(shù)指針只锭,也就知道了如何聲明一個代碼塊著恩。與函數(shù)指針類似,代碼塊具有以下特征:
    ① 返回類型可以手動聲明也可以由編譯器推導(dǎo)纹烹;
    ② 具有指定類型的參數(shù)列表页滚;
    ③ 擁有名稱召边。
    函數(shù)指針的聲明與代碼塊的聲明十分類似:


    image.png

    完整的代碼塊的定義以及代碼塊的內(nèi)容:


    image.png

    image.png

    所以铺呵,一個完整的代碼塊可以用如下關(guān)系式來表示它們:
    <returntype> (^blockname)(list of arguments) = ^(arguments){body;};
    ① 使用代碼塊
    image.png

    image.png

    ② 直接使用代碼塊
    image.png

    image.png

    ③ 使用typedef關(guān)鍵字
    image.png

    image.png

    ④ 代碼塊和變量
    代碼塊被聲明后會捕捉創(chuàng)建點時的狀態(tài)。代碼塊可以訪問函數(shù)用到的標準類型的變量:
    全局變量隧熙,包括在封閉范圍內(nèi)聲明的本地靜態(tài)變量片挂。
    全局函數(shù)(明顯不是真實的變量)。
    封閉范圍內(nèi)的參數(shù)贞盯。
    函數(shù)級別(即與代碼塊聲明時相同的級別)的__block變量音念。它們是可以修改的變量。
    封閉范圍內(nèi)的非靜態(tài)變量會被獲取為常量躏敢。
    Objective-C的實例變量闷愤。
    代碼塊內(nèi)部的本地變量。
    ⑤ 本地變量
    本地變量就是與代碼塊在同一范圍內(nèi)聲明的變量件余。


    image.png

    image.png

    第二個NSLog語句輸出200的原因: 變量是本地的讥脐,代碼塊會在定義時復(fù)制并保存它們的狀態(tài)。
    ⑥ 全局變量
    本地變量與代碼塊擁有相同的有效范圍啼器,你可以根據(jù)需要把變量標記為靜態(tài)的(全局的)旬渠。
    image.png

    image.png

    ⑦ 參數(shù)變量
    代碼塊中的參數(shù)變量與函數(shù)中的參數(shù)變量具有同樣的作用。
    ⑧ __block變量
    本地變量會被代碼塊作為常理獲取到端壳。如果你想修改它們的值告丢,必須將它們聲明為可修改的。
    image.png

    image.png

    有些變量是無法聲明為__block類型的损谦。它們有兩個限制:
    沒有長度可變的數(shù)組岖免;
    沒有包含可變長度數(shù)組的結(jié)構(gòu)體岳颇。
    ⑨ 代碼塊內(nèi)部的本地變量
    image.png

    image.png

    1.2 Objective-C 變量
    代碼塊是Objective-C語言中的優(yōu)秀公民,你可以像使用其他對象一樣使用它觅捆。使用時會遇到的最大問題就是內(nèi)存管理赦役。在代碼塊中訪問Objective-C變量時必須小心。
    以下規(guī)則能幫助你處理內(nèi)存管理栅炒。
    ① 如果引用了一個Objective-C對象掂摔,必須要保留它。
    ② 如果通過引用訪問了一個實例變量赢赊,要保留一次self(即執(zhí)行方法的對象)乙漓。
    ③ 如果通過數(shù)值訪問了一個實例變量,變量需要保留释移。
    注意:如果在代碼塊內(nèi)直接訪問實例變量, 則需要保留一次self, 即執(zhí)行方法的對象叭披。如果在代碼塊內(nèi)間接訪問實例變量,則變量本身需要保留玩讳。
    因為代碼塊是對象涩蜘,所以可以向它發(fā)送任何與內(nèi)存管理有關(guān)的消息。在C語言級別中熏纯,必須使用Block_cpoy()和Block_release()函數(shù)來適當(dāng)?shù)毓芾韮?nèi)存同诫。
  2. 并發(fā)性
    用來運行Xcode的Mac電腦的處理器至少擁有兩個核心,也可能更多≌晾剑現(xiàn)在最新的iOS設(shè)備都是多核的误窖。這意味著你可以在同一時間進行多項任務(wù)。蘋果公司提供了多種可以利用多核特性的API秩贰。能夠在同一時間執(zhí)行多項任務(wù)的程序稱為并發(fā)的程序霹俺。
    利用并發(fā)性最基礎(chǔ)的方法是使用POSIX線程來處理程序的不同部分使其能夠獨立執(zhí)行。POSIX線程擁有支持C語言和Objective-C的API毒费。編寫并發(fā)性程序需要創(chuàng)建多個線程丙唧,而編寫線程代碼是很有挑戰(zhàn)性的。因為線程是級別較低的API, 你必須手動管理觅玻。根據(jù)硬件和其他軟件運行的環(huán)境想际,需要的線程數(shù)量會發(fā)生變化。處理所有的線程是需要技巧的串塑。
    為了減輕在多核上編程的負擔(dān)沼琉,蘋果公司引入了GCD。這個技術(shù)較少了線程管理的麻煩桩匪。如果想要使用GCD, 你需要提交代碼塊或函數(shù)作為線程來運行打瘪。GCD是一個系統(tǒng)級別的技術(shù),因此你可以在任意級別的代碼塊中使用它。GCD決定需要多少線程并安排它們運行的速度闺骚。因為它是運行在系統(tǒng)級別上的彩扔,所以可以平衡應(yīng)用程序所有內(nèi)容的加載,這樣可以提高計算機或設(shè)備的執(zhí)行效率僻爽。
    2.1 同步
    我們?nèi)绾卧谟啥嗪私M成的通路中管理交通呢虫碉?可以使用同步裝置,比如在通道入口立一個標記(flag)或一個互斥(mutex)胸梆。
    注意:mutex是mutual exclusion的縮寫敦捧,它指的是確保兩個線程不會在同一時間進入臨界區(qū)。
    OC提供了一個語言級別的關(guān)鍵字@synchronized碰镜。這個關(guān)鍵字擁有一個參數(shù)兢卵,通常這個對象是可以修改的。它可以確保不同的線程會連續(xù)地訪問臨界區(qū)的代碼绪颖。
    nonatomic關(guān)鍵字修飾屬性秽荤,是為了提高性能,而如果用atomic修飾柠横,這樣設(shè)置代碼和變量會產(chǎn)生一些消耗窃款,它會比直接訪問更慢一些。
    ① 選擇性能
    如果你只想讓一些代碼在后臺執(zhí)行牍氛,NSObject也提供了方法晨继。這些方法的名字中都有performSelector:, 最簡單的就是performSelectorInBackground:withObject:了,它能在后臺執(zhí)行一個方法糜俗。它通過創(chuàng)建一個線程來運行方法踱稍。定義這些方法時必須遵從以下限制曲饱。
    1.這些方法運行在各自的線程里悠抹,因此你必須為這些Cocoa對象創(chuàng)建一個自動釋放池,而主自動釋放池是與主線程相關(guān)的扩淀。
    2.這些方法不能有返回值楔敌,并且要么沒有參數(shù),要么只有一個參數(shù)對象驻谆。換句話說卵凑,你只能使用以下代碼格式中的一種。
    代碼如下:


    1542597050043.jpg

    image.png

    當(dāng)方法執(zhí)行結(jié)束后胜臊,OC運行時會特地清理并棄掉線程勺卢。需要注意:方法執(zhí)行結(jié)束后并不會通知你:這是比較簡單的代碼。
    ② 調(diào)取隊列
    GCD可以使用調(diào)度隊列(dispatch queue)象对。它與線程很相似但使用起來更簡單黑忱。只需寫下你的代碼,把它指派為一個隊列,系統(tǒng)就會執(zhí)行它了甫煞。你可以同步或異步執(zhí)行任意代碼菇曲。一共有以下3種類型的隊列。
    連續(xù)隊列:每個連續(xù)隊列都會根據(jù)指派的順序執(zhí)行任務(wù)抚吠。你可以按自己的想法創(chuàng)建任意數(shù)量的隊列常潮,它們會并行操作任務(wù)。
    并發(fā)隊列:每個并發(fā)隊列都能并發(fā)執(zhí)行一個或多個任務(wù)楷力。任務(wù)會根據(jù)指派到隊列的順序開始執(zhí)行喊式。你無法創(chuàng)建連續(xù)隊列,只能從系統(tǒng)提供的3個隊列內(nèi)選擇一個來使用萧朝。
    主隊列:它是應(yīng)用程序中有效的主隊列垃帅,執(zhí)行的是應(yīng)用程序的主線程任務(wù)。
    一剪勿、連續(xù)隊列
    有時有一連串任務(wù)需要按照一定的順序執(zhí)行贸诚,這時便可以使用連續(xù)隊列。任務(wù)執(zhí)行順序為先入先出(FIFO): 只要任務(wù)是異步提交的厕吉,隊列會確保任務(wù)根據(jù)預(yù)定順序執(zhí)行酱固。這些隊列都是不會發(fā)生死鎖的。
    死鎖(deadlock)是一個令人不悅的情況头朱,指的是兩個或多個任務(wù)在等待他方運行結(jié)束运悲。


    image.png

    二、并發(fā)隊列
    并發(fā)隊列適用于那些可以并行運行的任務(wù)项钮。并發(fā)隊列也遵從先入先出(FIFO)的規(guī)范班眯,且任務(wù)可以在前一個任務(wù)結(jié)束前就開始執(zhí)行。一次所運行的任務(wù)數(shù)量是無法預(yù)測的烁巫,它會根據(jù)其他運行的任務(wù)在不同時間變化署隘。所以每次你運行同一個程序,并發(fā)任務(wù)的數(shù)量可能會是不一樣的亚隙。
    注意:如果需要確保每次運行的任務(wù)數(shù)量都是一樣的磁餐,可以通過線程API來手動管理線程。
    每個應(yīng)用程序都有3種并發(fā)隊列可以使用:高優(yōu)先級(high)阿弃、默認優(yōu)先級(default)和低優(yōu)先級(low)诊霹。
    image.png

    三、主隊列
    image.png

    因為這個隊列與主線程有關(guān)渣淳,所以必須小心安排這個隊列中的任務(wù)順序脾还,否則它們可能會阻塞主應(yīng)用程序運行。通常要以同步方式使用這個隊列入愧,提交多個任務(wù)并在它們操作完畢后執(zhí)行一些動作鄙漏。
    四赛蔫、獲取當(dāng)前隊列
    image.png

    2.2 隊列也有內(nèi)存管理
    調(diào)度隊列是引用計數(shù)對象∧嗾牛可以使用dispatch_retain()和dispatch_release()來修改隊列的保留計數(shù)器的值呵恢。它們與一般對象的retain和release語句類似。你只能對你自己創(chuàng)建的隊列使用這些函數(shù)媚创,而無法用在全局調(diào)度隊列上渗钉。事實上,如果你向全局隊列發(fā)送這些消息钞钙,它們會直接被忽略掉鳄橘,所以即使這樣做也是無害的。如果你編寫的是一個使用了垃圾回收的OSX應(yīng)用程序芒炼,那么你必須手動管理這些隊列瘫怜。
    2.2.1 隊列的上下文
    你可以向調(diào)度對象(包括調(diào)度隊列)指派全局數(shù)據(jù)上下文,可以在上下文中指派任意類型的數(shù)據(jù)本刽,比如OC對象或指針鲸湃。系統(tǒng)只能知道上下文包含了與隊列有關(guān)的數(shù)據(jù),上下文數(shù)據(jù)的內(nèi)存管理只能由你來做子寓。你必須在需要它的時候分配內(nèi)存并在隊列銷毀之前進行清理暗挑。在為上下文數(shù)據(jù)分配內(nèi)存的時候,可以使用dispatch_set_context()和dispatch_get_context()函數(shù)斜友。
    image.png

    ① 清理函數(shù)

    設(shè)置完上下文對象的數(shù)據(jù)之后炸裆,什么時候清理呢?你不需要真得知道上下文對象在何時何地會被棄用鲜屏。如果想解決上下文對象的清理問題烹看,你可以讓對象在它棄用的時候調(diào)用一個函數(shù),就像類里面的dealloc函數(shù)洛史。函數(shù)的格式應(yīng)該如下所示惯殊。(道理是一樣的,不需要知道它何時何地棄用或者銷毀虹菲,但只需要在它棄用或者銷毀的函數(shù)里進行相應(yīng)內(nèi)存管理的處理即可靠胜。)


    image.png

    1542621894089.jpg

    ② 添加任務(wù)
    有兩種方式可以向隊列中添加任務(wù)
    同步:隊列會一直等待前面任務(wù)結(jié)束掉瞳。
    異步:添加任務(wù)后毕源,不必等待任務(wù),函數(shù)會立刻返回陕习。推薦優(yōu)先使用這種方式霎褐,因為它不會阻塞其他代碼的運行。
    你可以選擇向隊列提交代碼塊或函數(shù)该镣。一共有4個調(diào)度函數(shù)冻璃,分別是代碼塊和函數(shù)各自的同步與異步方式。
    注意:如果想要避免出現(xiàn)死鎖,那么絕對不要給運行在同一隊列中的任務(wù)調(diào)用dispatch_sync或dispatch_sync_f函數(shù)省艳。
    2.2.2 調(diào)度程序
    添加任務(wù)最簡單的方法就是通過代碼塊娘纷。
    image.png

    image.png

    image.png

    2.3 操作隊列
    被稱為操作的API, 可以讓隊列在OC層級上使用起來更加簡單。
    如果想要使用操作跋炕,首先需要創(chuàng)建一個操作對象赖晶,然后將其指派給操作隊列,并讓隊列執(zhí)行它辐烂。一共有3種創(chuàng)建操作的方式遏插。
    ① NSInvocationOperation: 如果你已經(jīng)擁有一個可以完成工作的類,并且想要在隊列上執(zhí)行纠修,可以嘗試使用這方法胳嘲。
    ② NSBlockOperation: 這有些像包含了需要執(zhí)行代碼塊的dispatch_async函數(shù)。
    ③ 自定義的操作:如果你需要更靈活的操作類型扣草,可以創(chuàng)建自己的自定義類型了牛。你必須通過NSOperation子類來定義你的操作。
    創(chuàng)建調(diào)用操作(invocation operation)
    image.png

    image.png

    創(chuàng)建代碼塊操作
    image.png

    image.png

    向隊列中添加操作
    一旦創(chuàng)建了操作辰妙,你就需要向隊列中添加代碼塊白魂。這次我們將使用NSOperationQueue來取代之前使用的dispatch_queue_t函數(shù)。NSOperationQueue一般會并發(fā)執(zhí)行操作上岗。它具有相關(guān)性福荸,因此如果某操作是基于其他操作的,它們會相應(yīng)地執(zhí)行肴掷。
    如果要確保你的操作是連續(xù)執(zhí)行的敬锐,可以設(shè)置最大并發(fā)操作數(shù)為1,這樣任務(wù)將會按照先入先出的規(guī)范執(zhí)行呆瞻。在向隊列添加操作之前台夺,需要某個方法來引用到那個隊列。你可以創(chuàng)建一個新隊列或使用之前已經(jīng)定義過的隊列痴脾。
    image.png

    小結(jié):代碼塊是OC的新特性颤介,增強了函數(shù)的功能。有了代碼塊赞赖,就可以通過綁定變量來創(chuàng)建程序中會使用到的對象滚朵。代碼塊在實現(xiàn)并發(fā)性功能時尤其方便。
    并發(fā)性很復(fù)雜前域,本章僅討論對OSX和iOS程序有效的并發(fā)性功能辕近。
    蘋果公司的GCD特性提供了一種方法,你無需花很多時間在系統(tǒng)的低層級編碼匿垄,應(yīng)用程序就可以使用并發(fā)性移宅。你應(yīng)該多嘗試GCD和其他并發(fā)性編程功能归粉,以找出哪些對于你的應(yīng)用程序是可行的,哪些很好用漏峰。
    隨著你的水平不斷提高以及蘋果公司添加更多的工具糠悼,你的應(yīng)用程序?qū)⒛軌虿⑿袌?zhí)行更多的任務(wù),從而更快地做出響應(yīng)浅乔。不過绢掰,一旦超過了臨界點,給應(yīng)用程序添加并行的任務(wù)就會得不償失童擎。(花大量時間編碼和調(diào)試)
    如果你經(jīng)常要使用并發(fā)任務(wù)滴劲,請避免發(fā)生死鎖。(任務(wù)相互關(guān)聯(lián)導(dǎo)致程序永遠無法結(jié)束)或出現(xiàn)其他麻煩的Bug顾复。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末班挖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子芯砸,更是在濱河造成了極大的恐慌萧芙,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件假丧,死亡現(xiàn)場離奇詭異双揪,居然都是意外死亡,警方通過查閱死者的電腦和手機包帚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門渔期,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渴邦,你說我怎么就攤上這事疯趟。” “怎么了谋梭?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵信峻,是天一觀的道長。 經(jīng)常有香客問我瓮床,道長盹舞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任隘庄,我火速辦了婚禮踢步,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘峭沦。我一直安慰自己贾虽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布吼鱼。 她就那樣靜靜地躺著蓬豁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菇肃。 梳的紋絲不亂的頭發(fā)上地粪,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音琐谤,去河邊找鬼蟆技。 笑死,一個胖子當(dāng)著我的面吹牛斗忌,可吹牛的內(nèi)容都是我干的质礼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼织阳,長吁一口氣:“原來是場噩夢啊……” “哼眶蕉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唧躲,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤造挽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弄痹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饭入,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年肛真,在試婚紗的時候發(fā)現(xiàn)自己被綠了谐丢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚓让,死狀恐怖庇谆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凭疮,我是刑警寧澤饭耳,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站执解,受9級特大地震影響寞肖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衰腌,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一新蟆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧右蕊,春花似錦琼稻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸠补。三九已至,卻和暖如春嘀掸,著一層夾襖步出監(jiān)牢的瞬間紫岩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工睬塌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泉蝌,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓揩晴,卻偏偏與公主長得像勋陪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硫兰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容