• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

      Block分析

      2016-12-21 07:20:53許莉鑫金海佳李瑪田英愛(ài)
      數(shù)碼世界 2016年12期
      關(guān)鍵詞:聲明自動(dòng)變量

      許莉鑫 金海佳 李瑪 田英愛(ài)

      北京信息科技大學(xué)計(jì)算機(jī)學(xué)院

      Block分析

      許莉鑫 金海佳 李瑪 田英愛(ài)

      北京信息科技大學(xué)計(jì)算機(jī)學(xué)院

      Block是蘋(píng)果公司在iOS4后引入的對(duì)C語(yǔ)言的擴(kuò)展。把Block的功能概括來(lái)說(shuō),即帶有自動(dòng)變量(即局部變量)的匿名函數(shù)指針。本文將對(duì)Block在這幾個(gè)方面進(jìn)行解讀:一、Block的語(yǔ)法。二、Block作為函數(shù)參數(shù)使用的方法。三、Block對(duì)自動(dòng)變量的截獲。四、__Block說(shuō)明符、存儲(chǔ)域。五、循環(huán)引用導(dǎo)致內(nèi)存泄漏的問(wèn)題。本文旨在使閱讀者深入認(rèn)識(shí)Block并更好地使用Block。

      Block Objective-C iOS

      1 引言

      在編程中閉包是非常常見(jiàn)的一種技術(shù)手段,在Objective-C中被稱(chēng)做Block。Block因其簡(jiǎn)潔的語(yǔ)法,特殊的存儲(chǔ)方式,被廣泛地使用在Objective-C工程中。很好地使用Block并不簡(jiǎn)單,本文將針對(duì)Block進(jìn)行深入分析。

      Block本質(zhì)是一個(gè)函數(shù)指針,它的使用方法和C語(yǔ)言函數(shù)指針一樣,可以傳入?yún)?shù),且有返回值。但和函數(shù)指針相比,Block功能更強(qiáng)大,所以Block也復(fù)雜很多,它與函數(shù)指針的區(qū)別主要表現(xiàn)在以下方面:語(yǔ)法上存在區(qū)別、Block是一個(gè)匿名指針、Block會(huì)截獲自動(dòng)變量、內(nèi)存管理與釋放的區(qū)別。

      2 Block語(yǔ)法

      2.1 聲明Block

      在C語(yǔ)言中,可以將一個(gè)函數(shù)的地址賦值給函數(shù)指針類(lèi)型變量中,形式如:

      int functionName(int count){

      return count;

      因?yàn)锽lock本質(zhì)是一個(gè)匿名函數(shù)指針,所以聲明一個(gè)Block和C語(yǔ)言中聲明函數(shù)指針十分類(lèi)似,形式如:

      與C語(yǔ)言中聲明函數(shù)指針相比,聲明Block的區(qū)別即將“*”替換成。

      Block類(lèi)型變量和一般的C語(yǔ)言變量的使用方法完全相同,它可作為自動(dòng)變量、函數(shù)參數(shù)、靜態(tài)變量、靜態(tài)全局變量、全局變量等使用。

      2.2 對(duì)Block賦值

      形式如:

      “^”符號(hào)表明這是一個(gè)Block,“^”后的括號(hào)中包含著參數(shù),花括號(hào)中可以進(jìn)行一些操作,并根據(jù)需要在確定時(shí)候返回。

      2.3 使用Block

      可以像使用一個(gè)C語(yǔ)言函數(shù)一樣來(lái)使用Block:

      int count = blo(10);

      3 Block可作為函數(shù)參數(shù)

      Block比C語(yǔ)言中的函數(shù)強(qiáng)大,比如Block可以作為函數(shù)參數(shù)??梢杂靡韵路绞铰暶饕粋€(gè)Objective-C的方法:

      然后以以下方式調(diào)用這個(gè)方法:

      這里hander變成了回調(diào),事實(shí)上Apple的大量api接口也是這么設(shè)計(jì)的。在functionName方法中也許進(jìn)行了大量的計(jì)算,開(kāi)辟了很多線程,等待了很長(zhǎng)的時(shí)間,但所有這些復(fù)雜的過(guò)程對(duì)于用戶(hù)(方法的使用者)來(lái)說(shuō)都是不關(guān)心的,用戶(hù)關(guān)心的只有在hander中返回的“count”參數(shù)。

      這個(gè)方法可以被寫(xiě)得更加漂亮,即添加一個(gè)Block類(lèi)型變量,這其中用到C語(yǔ)言中的typedef。

      typedef void(blo)(int count);

      上例給帶有“count”參數(shù)的閉包起了一個(gè)blo的別名,所以在接下來(lái)的函數(shù)聲明中就可以使用blo來(lái)代替原本的參數(shù)類(lèi)型,如下:

      -(void)functionName:(blo)hander;

      4 Block的截獲自動(dòng)變量特性

      以以下代碼為例,

      此例中,blo();執(zhí)行時(shí)控制臺(tái)將輸出“I am Eric”,即便name代表的字符串在Block后已被修改成“I am Strong”。這就是Block對(duì)自動(dòng)變量的截獲,簡(jiǎn)單來(lái)說(shuō),Block對(duì)自動(dòng)變量的截獲是指在編譯Block時(shí),Block會(huì)保存(截獲)其中使用到的變量,不論Block中的變量的值在其后的語(yǔ)句中是否會(huì)被修改,Block中記錄的該變量的值永遠(yuǎn)不會(huì)改變。

      5 關(guān)于使用__block說(shuō)明符

      Block對(duì)自動(dòng)變量的截獲只能用于獲取變量的值,而不能對(duì)其進(jìn)行更改。當(dāng)嘗試去更改截獲的自動(dòng)變量值的時(shí)候,編譯器將報(bào)錯(cuò)。例如下面這種情況,

      此時(shí),編譯器會(huì)報(bào)出以下錯(cuò)誤:

      Variable is not assignable (missing __block type specifier)

      這個(gè)錯(cuò)誤提示我們,若想在Block中修改截獲的自動(dòng)變量的值,則需給變量加上“__Block”修飾符,如下所示,

      使用附有__Block說(shuō)明符的自動(dòng)變量可以在Block中賦值,該變量稱(chēng)為_(kāi)_block變量。

      再舉一例,

      上例在Block中對(duì)arr變量進(jìn)行了初始化的賦值操作,執(zhí)行會(huì)發(fā)生錯(cuò)誤,同樣需要給arr變量加__block修飾符來(lái)解決。

      但不是所有在Block中變更的對(duì)象都需要加上__Block說(shuō)明符。如果在Block中僅對(duì)OC對(duì)象進(jìn)行操作,而不對(duì)其進(jìn)行賦值,這樣的變更就不會(huì)報(bào)錯(cuò),故無(wú)需加上__Block說(shuō)明符。例如,

      此例截獲的變量是一個(gè)NSMutableArray類(lèi)型的變量,Block中對(duì)一個(gè)可變數(shù)組進(jìn)行了操作,而沒(méi)有進(jìn)行賦值,所以可以正常執(zhí)行。

      用C語(yǔ)言指針來(lái)解釋以上情形,即未附有__Block說(shuō)明符的自動(dòng)變量不能在Block中更改變量指針的指向,但可以對(duì)變量進(jìn)行操作(改變地址內(nèi)容)。

      談到C語(yǔ)言指針,還要注意在Block中對(duì)C語(yǔ)言數(shù)組的使用方法。例如:

      執(zhí)行上面這段代碼,編譯器會(huì)發(fā)出以下錯(cuò)誤:

      Cannot refer to declaration with an array type inside block

      Implicit conversion of an Objective-C pointer to ‘const char *’ is disallowed with ARC

      這是因?yàn)樵诂F(xiàn)在的Block中,截獲自動(dòng)變量的方法沒(méi)有實(shí)現(xiàn)對(duì)C語(yǔ)言的截獲。對(duì)于這個(gè)問(wèn)題,可以使用指針來(lái)解決,如下:

      6 Block存儲(chǔ)域

      存儲(chǔ)域一共分為三種:_NSConcreteStackBlock、_ NSConcreteGlobalBlock、_NSConcreteMallocBlock。即“棧存儲(chǔ)域”、“全局存儲(chǔ)域”、“堆存儲(chǔ)域”。Block與OC變量不同,它不全存儲(chǔ)在“棧存儲(chǔ)域”。

      a.Block存儲(chǔ)在“全局作用域”中的情況:

      如上所示,當(dāng)我們聲明一個(gè)全局的Block,Block就將被存儲(chǔ)在_NSConcreteGlobalBlock中。因?yàn)檫@種情況下在Block中無(wú)法對(duì)自動(dòng)變量進(jìn)行截獲,即Block的內(nèi)容不依賴(lài)于運(yùn)行時(shí)的狀態(tài),因此將Block放在“全局作用域”中是最合適的。事實(shí)上,只要Block的內(nèi)容不依賴(lài)于運(yùn)行時(shí)的狀態(tài),也就是不對(duì)自動(dòng)變量進(jìn)行截獲,那么不管Block的聲明實(shí)現(xiàn)位置在哪,這個(gè)Block都將被存儲(chǔ)在“全局作用域”當(dāng)中。

      b.Block存儲(chǔ)在“堆作用域”中的情況:

      當(dāng)將Block作為回調(diào)使用時(shí),可以發(fā)現(xiàn)當(dāng)Block超出了塊作用域時(shí)仍可以被使用,例如在網(wǎng)絡(luò)回調(diào)中:

      我們經(jīng)常會(huì)使用類(lèi)似上面這種方式進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求,在Block中對(duì)請(qǐng)求返回?cái)?shù)據(jù)進(jìn)行處理。由于網(wǎng)絡(luò)請(qǐng)求是一個(gè)異步過(guò)程,所以在請(qǐng)求返回之后,已經(jīng)超出了Block的作用域。之所以這種情況下Block仍可以被使用,是因?yàn)檫@種情況下Block將被復(fù)制在“堆存儲(chǔ)域”中,包括Block中的自動(dòng)變量也將會(huì)被拷貝到堆存儲(chǔ)域當(dāng)中。

      還有一種情況是當(dāng)將Block作為函數(shù)返回值返回時(shí),Block同樣會(huì)被拷貝到“堆存儲(chǔ)域”中,再來(lái)進(jìn)行返回。

      大多數(shù)情況下,XCode(IDE)會(huì)自動(dòng)幫編程者判斷Block在什么情況下需要被拷貝到“堆存儲(chǔ)域”中,但是在某些情況下編程者需要手動(dòng)進(jìn)行這個(gè)過(guò)程,使用“copy”命令把Block從“?!笨截惖健岸选敝?。

      7 Block循環(huán)作用域

      前文提到Block在引用自動(dòng)變量時(shí)將把變量從棧中拷貝到堆中,所以,比如當(dāng)拷貝__strong屬性變量時(shí),十分容易引起循環(huán)引用,進(jìn)而造成內(nèi)存泄漏。下面這段代碼就會(huì)引起循環(huán)引用:

      其中ViewController持有一個(gè)變量Block,但在Block中再次截獲了self,也就是Block持有self,ViewController的釋放需要Block來(lái)釋放self,而B(niǎo)lock同樣需要ViewController釋放才會(huì)釋放,這是標(biāo)準(zhǔn)的循環(huán)引用。解決這個(gè)問(wèn)題可以使用__weak說(shuō)明符,如下:

      當(dāng)使用__weak說(shuō)明符后,Block不再持有self,于是打破了循環(huán)引用。

      事實(shí)上并不是在Block中顯示的出現(xiàn)self以后才會(huì)發(fā)生循環(huán)引用,下面這種情況也會(huì)發(fā)生循環(huán)引用:

      上例Block中沒(méi)有出現(xiàn)self,但在這種情況下也會(huì)發(fā)生循環(huán)引用。原因是這種情況雖然沒(méi)有使用get方法來(lái)獲取變量,但是直接通過(guò)內(nèi)存地址獲取了變量,等同于以下代碼:

      這解釋了為什么第二種情況也會(huì)發(fā)生循環(huán)應(yīng)用。解決這樣的循環(huán)引用,同樣可以使用__weak說(shuō)明符:

      需要注意的是,如果一個(gè)Block在運(yùn)行時(shí)沒(méi)有被調(diào)用,但是在Block中發(fā)生了循環(huán)引用,就也會(huì)發(fā)生內(nèi)存泄漏。原因是Block將自動(dòng)變量拷貝到“堆存儲(chǔ)域”的動(dòng)作是在編譯時(shí)期完成的,所以即便沒(méi)有調(diào)用Block,XCode也已經(jīng)在編譯時(shí)期將自動(dòng)變量拷貝到了“堆存儲(chǔ)域”當(dāng)中。

      解決Block的循環(huán)引用問(wèn)題的方法除以上所述使用__weak說(shuō)明符外,還有另外一種方式。為了解決循環(huán)引用我們必須打破雙方其中一方的引用,所以上例中使用了__weak說(shuō)明符,但下面的代碼也可以達(dá)到相同的目的:

      以上代碼中聲明了一個(gè)名為myObject的類(lèi),這個(gè)類(lèi)中的Block發(fā)生了循環(huán)引用,如果聲明了這個(gè)類(lèi)的一個(gè)實(shí)列對(duì)象,那么這么對(duì)象因?yàn)檠h(huán)引用而不會(huì)被釋放。

      如上,當(dāng)聲明一個(gè)Object的myObject類(lèi)后,Object就已經(jīng)發(fā)生了內(nèi)存泄漏,但是如果在合適的位置來(lái)釋放Block就可以解決這個(gè)問(wèn)題:

      如上,當(dāng)將Block置空以后,block就失去了對(duì)Object的引用,所以這種情況不會(huì)再發(fā)生循環(huán)引用。但這樣直接將Block置空的方式是十分危險(xiǎn)的,因?yàn)楦淖兞薆lock初始化的值,后面的代碼運(yùn)行結(jié)果就可能不同于所預(yù)料的了。所以選擇置空Block的時(shí)刻非常關(guān)鍵。

      8 總結(jié)

      本文從Block的語(yǔ)法,Block作為函數(shù)參數(shù)使用的方法,Block對(duì)自動(dòng)變量的截獲,Block的使用方式:__Block說(shuō)明符、存儲(chǔ)域這些方面全面介紹了Block,并針對(duì)因循環(huán)引用導(dǎo)致內(nèi)存泄漏的問(wèn)題提出了解決辦法。通過(guò)本文幫助讀者深入認(rèn)識(shí)Block并更好地理解Block。

      [1] Kazuki Sakamoto,Tomohiko Furumoto.Objective-C 高級(jí)編程.人民郵電出版社,2013-06-01

      本項(xiàng)目由北京信息科技大學(xué)2016年人才培養(yǎng)質(zhì)量提高經(jīng)費(fèi)(5111610800)支持。

      猜你喜歡
      聲明自動(dòng)變量
      本刊聲明
      本刊聲明
      抓住不變量解題
      也談分離變量
      自動(dòng)捕盜機(jī)
      本刊聲明
      本刊聲明
      基于STM32的自動(dòng)喂養(yǎng)機(jī)控制系統(tǒng)
      關(guān)于自動(dòng)駕駛
      SL(3,3n)和SU(3,3n)的第一Cartan不變量
      清河县| 达拉特旗| 华坪县| 遵义县| 大名县| 黄龙县| 建瓯市| 景泰县| 天津市| 安康市| 阜城县| 渭源县| 益阳市| 锡林浩特市| 泰兴市| 寿宁县| 通许县| 永吉县| 连州市| 霍州市| 辉县市| 平果县| 金湖县| 定日县| 凤阳县| 商丘市| 双辽市| 南靖县| 九龙县| 班玛县| 高唐县| 衡阳市| 闽侯县| 南和县| 庆云县| 荆州市| 彭山县| 贵港市| 延安市| 平遥县| 通化市|