999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

Block分析

2016-12-21 07:20:53許莉鑫金海佳李瑪田英愛(ài)
數(shù)碼世界 2016年12期

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

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

Block分析

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

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

Block是蘋果公司在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 引言

在編程中閉包是非常常見的一種技術(shù)手段,在Objective-C中被稱做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ù)指針類型變量中,形式如:

int functionName(int count){

return count;

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

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

Block類型變量和一般的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ì)算,開辟了很多線程,等待了很長(zhǎng)的時(shí)間,但所有這些復(fù)雜的過(guò)程對(duì)于用戶(方法的使用者)來(lái)說(shuō)都是不關(guān)心的,用戶關(guān)心的只有在hander中返回的“count”參數(shù)。

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

typedef void(blo)(int count);

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

-(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中賦值,該變量稱為__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類型的變量,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)容不依賴于運(yùn)行時(shí)的狀態(tài),因此將Block放在“全局作用域”中是最合適的。事實(shí)上,只要Block的內(nè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ì)使用類似上面這種方式進(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,而Block同樣需要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的類,這個(gè)類中的Block發(fā)生了循環(huán)引用,如果聲明了這個(gè)類的一個(gè)實(shí)列對(duì)象,那么這么對(duì)象因?yàn)檠h(huán)引用而不會(huì)被釋放。

如上,當(dāng)聲明一個(gè)Object的myObject類后,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)支持。

主站蜘蛛池模板: 久久久久九九精品影院| 欧美激情综合一区二区| 亚洲VA中文字幕| 99久久成人国产精品免费| 毛片基地美国正在播放亚洲 | 综合色亚洲| 久久网综合| 91精品国产91久无码网站| 欧美午夜理伦三级在线观看| 在线观看热码亚洲av每日更新| 亚洲国产欧洲精品路线久久| 综合天天色| 69精品在线观看| 欧美午夜在线观看| 欧美精品高清| 欧美成在线视频| 久草视频中文| 国产精品永久久久久| 亚洲国产天堂久久综合| 中文字幕无码av专区久久| 成人一级免费视频| 日韩无码黄色| 91免费国产高清观看| 欧美精品亚洲日韩a| 久久综合色88| 免费va国产在线观看| 亚洲美女操| 日韩毛片在线播放| 日韩免费无码人妻系列| 国产在线无码av完整版在线观看| 无码精油按摩潮喷在线播放| 国产在线精品99一区不卡| 欧美第二区| 四虎精品国产AV二区| 午夜精品国产自在| 天天躁狠狠躁| 天天操精品| 亚洲欧美一级一级a| 欧美三级不卡在线观看视频| 26uuu国产精品视频| 黑人巨大精品欧美一区二区区| 狠狠色狠狠综合久久| 四虎AV麻豆| 国产AV无码专区亚洲A∨毛片| 国产综合精品日本亚洲777| 成人一区专区在线观看| 91在线播放免费不卡无毒| 国产乱子伦精品视频| 久久精品娱乐亚洲领先| 在线精品亚洲国产| 日韩精品亚洲人旧成在线| 国产偷倩视频| 91人妻在线视频| 人妻21p大胆| 亚洲日韩AV无码一区二区三区人| 精品少妇三级亚洲| 欧美天堂在线| 51国产偷自视频区视频手机观看| 日韩黄色精品| 欧美成人日韩| 日本AⅤ精品一区二区三区日| 成人国产精品一级毛片天堂 | 在线视频亚洲色图| 国产成人无码AV在线播放动漫| 一本色道久久88亚洲综合| 免费看a级毛片| 欧美视频在线第一页| 就去吻亚洲精品国产欧美| 国产色网站| 99精品国产高清一区二区| 国产成人无码Av在线播放无广告| 久久人与动人物A级毛片| 在线观看91香蕉国产免费| 国产高清在线精品一区二区三区 | 亚洲愉拍一区二区精品| 人与鲁专区| 国产成人亚洲无吗淙合青草| 亚洲永久免费网站| 欧美日韩免费观看| 激情综合婷婷丁香五月尤物| 亚洲va在线∨a天堂va欧美va| 欧美在线免费|