久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

iOS中block的探究

 許多潤澤 2012-11-23

iOS中block的探究

Block是iOS4.0+ 和Mac OS X 10.6+ 引進(jìn)的對C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,。

文/CocoaChina社區(qū)會員casual0402

/* ---------------------------------------------------------------------------------------------------- */

[0. Brief introduction of block]

Block是iOS4.0+ 和Mac OS X 10.6+ 引進(jìn)的對C語言的擴(kuò)展,,用來實(shí)現(xiàn)匿名函數(shù)的特性。

用維基百科的話來說,,Block是Apple Inc.為C,、C++以及Objective-C添加的特性,使得這些語言可以用類lambda表達(dá)式的語法來創(chuàng)建閉包,。

用Apple文檔的話來說,,A block is an anonymous inline collection of code, and sometimes also called a "closure".

關(guān)于閉包,我覺得阮一峰的一句話解釋簡潔明了:閉包就是能夠讀取其它函數(shù)內(nèi)部變量的函數(shù),。

這個解釋用到block來也很恰當(dāng):一個函數(shù)里定義了個block,這個block可以訪問該函數(shù)的內(nèi)部變量,。

一個簡單的Block示例如下:

int (^maxBlock)(int, int) = ^(int x, int y) { return x > y ? x : y; };

如果用Python的lambda表達(dá)式來寫,,可以寫成如下形式:

f = lambda x, y : x if x > y else y

不過由于Python自身的語言特性,在def定義的函數(shù)體中,,可以很自然地再用def語句定義內(nèi)嵌函數(shù),,因?yàn)檫@些函數(shù)本質(zhì)上都是對象。

如果用BNF來表示block的上下文無關(guān)文法,,大致如下:

block_expression  ::=  ^  block_declare  block_statement

block_declare  ::=  block_return_type  block_argument_list

block_return_type ::=  return_type  |  空

block_argument_list  ::=  argument_list  |  空

 

/* ---------------------------------------------------------------------------------------------------- */

[1. Why block]

Block除了能夠定義參數(shù)列表,、返回類型外,還能夠獲取被定義時(shí)的詞法范圍內(nèi)的狀態(tài)(比如局部變量),,并且在一定條件下(比如使用__block變量)能夠修改這些狀態(tài),。此外,這些可修改的狀態(tài)在相同詞法范圍內(nèi)的多個block之間是共享的,,即便出了該詞法范圍(比如棧展開,,出了作用域),,仍可以繼續(xù)共享或者修改這些狀態(tài)。

通常來說,,block都是一些簡短代碼片段的封裝,,適用作工作單元,通常用來做并發(fā)任務(wù),、遍歷,、以及回調(diào)。

比如我們可以在遍歷NSArray時(shí)做一些事情:

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;

其中將stop設(shè)為YES,,就跳出循環(huán),,不繼續(xù)遍歷了。

而在很多框架中,,block越來越經(jīng)常被用作回調(diào)函數(shù),,取代傳統(tǒng)的回調(diào)方式。

用block作為回調(diào)函數(shù),,可以使得程序員在寫代碼更順暢,,不用中途跑到另一個地方寫一個回調(diào)函數(shù),有時(shí)還要考慮這個回調(diào)函數(shù)放在哪里比較合適,。采用block,,可以在調(diào)用函數(shù)時(shí)直接寫后續(xù)處理代碼,將其作為參數(shù)傳遞過去,,供其任務(wù)執(zhí)行結(jié)束時(shí)回調(diào),。

另一個好處,就是采用block作為回調(diào),,可以直接訪問局部變量,。比如我要在一批用戶中修改一個用戶的name,修改完成后通過回調(diào)更新對應(yīng)用戶的單元格UI,。這時(shí)候我需要知道對應(yīng)用戶單元格的index,,如果采用傳統(tǒng)回調(diào)方式,要嘛需要將index帶過去,,回調(diào)時(shí)再回傳過來,;要嘛通過外部作用域記錄當(dāng)前操作單元格的index(這限制了一次只能修改一個用戶的name);要嘛遍歷找到對應(yīng)用戶,。而使用block,,則可以直接訪問單元格的index。

這份文檔中提到block的幾種適用場合:

任務(wù)完成時(shí)回調(diào)
處理消息監(jiān)聽回調(diào)處理
錯誤回調(diào)處理
枚舉回調(diào)
視圖動畫,、變換
排序

 

/* ---------------------------------------------------------------------------------------------------- */

[2. About __block_impl]

Clang提供了中間代碼展示的選項(xiàng)供我們進(jìn)一步了解block的原理,。

以一段很簡單的代碼為例:

使用-rewrite-objc選項(xiàng)編譯:

得到一份block0.cpp文件,在這份文件中可以看到如下代碼片段:

從命名可以看出這是block的實(shí)現(xiàn),,并且得知block在Clang編譯器前端得到實(shí)現(xiàn),,可以生成C中間代碼,。很多語言都可以只實(shí)現(xiàn)編譯器前端,生成C中間代碼,,然后利用現(xiàn)有的很多C編譯器后端,。

從結(jié)構(gòu)體的成員可以看出,F(xiàn)lags,、Reserved可以先略過,,isa指針表明了block可以是一個NSObject,而FuncPtr指針顯然是block對應(yīng)的函數(shù)指針,。

由此,,揭開了block的神秘面紗。

不過,,block相關(guān)的變量放哪里呢,?上面提到block可以capture詞法范圍內(nèi)(或者說是外層上下文、作用域)的狀態(tài),,即便是出了該范圍,,仍然可以修改這些狀態(tài)。這是如何做到的呢,?

 

/* ---------------------------------------------------------------------------------------------------- */

[3. Implementation of a simple block]

先看一個只輸出一句話的block是怎么樣的,。

生成中間代碼,得到片段如下:

首先出現(xiàn)的結(jié)構(gòu)體就是__main_block_impl_0,,可以看出是根據(jù)所在函數(shù)(main函數(shù))以及出現(xiàn)序列(第0個)進(jìn)行命名的,。如果是全局block,就根據(jù)變量名和出現(xiàn)序列進(jìn)行命名,。__main_block_impl_0中包含了兩個成員變量和一個構(gòu)造函數(shù),,成員變量分別是__block_impl結(jié)構(gòu)體和描述信息Desc,之后在構(gòu)造函數(shù)中初始化block的類型信息和函數(shù)指針等信息,。

接著出現(xiàn)的是__main_block_func_0函數(shù),,即block對應(yīng)的函數(shù)體。該函數(shù)接受一個__cself參數(shù),,即對應(yīng)的block自身,。

再下面是__main_block_desc_0結(jié)構(gòu)體,,其中比較有價(jià)值的信息是block大小,。

最后就是main函數(shù)中對block的創(chuàng)建和調(diào)用,可以看出執(zhí)行block就是調(diào)用一個以block自身作為參數(shù)的函數(shù),,這個函數(shù)對應(yīng)著block的執(zhí)行體,。

這里,block的類型用_NSConcreteStackBlock來表示,,表明這個block位于棧中,。同樣地,,還有_NSConcreteMallocBlock和_NSConcreteGlobalBlock。

由于block也是NSObject,,我們可以對其進(jìn)行retain操作,。不過在將block作為回調(diào)函數(shù)傳遞給底層框架時(shí),底層框架需要對其copy一份,。比方說,,如果將回調(diào)block作為屬性,不能用retain,,而要用copy,。我們通常會將block寫在棧中,而需要回調(diào)時(shí),,往往回調(diào)block已經(jīng)不在棧中了,,使用copy屬性可以將block放到堆中?;蛘呤褂肂lock_copy()和Block_release(),。

 

/* ---------------------------------------------------------------------------------------------------- */

[4. Capture local variable]

再看一個訪問局部變量的block是怎樣的。

生成中間代碼,,得到片段如下:

可以看出這次的block結(jié)構(gòu)體__main_block_impl_0多了個成員變量i,,用來存儲使用到的局部變量i(值為1024);并且此時(shí)可以看到__cself參數(shù)的作用,,類似C++中的this和Objective-C的self,。

如果我們嘗試修改局部變量i,則會得到如下錯誤:

錯誤信息很詳細(xì),,既告訴我們變量不可賦值,,也提醒我們要使用__block類型標(biāo)識符。

為什么不能給變量i賦值呢,?

因?yàn)閙ain函數(shù)中的局部變量i和函數(shù)__main_block_func_0不在同一個作用域中,,調(diào)用過程中只是進(jìn)行了值傳遞。當(dāng)然,,在上面代碼中,,我們可以通過指針來實(shí)現(xiàn)局部變量的修改。不過這是由于在調(diào)用__main_block_func_0時(shí),,main函數(shù)棧還沒展開完成,,變量i還在棧中。但是在很多情況下,,block是作為參數(shù)傳遞以供后續(xù)回調(diào)執(zhí)行的,。通常在這些情況下,block被執(zhí)行時(shí),定義時(shí)所在的函數(shù)棧已經(jīng)被展開,,局部變量已經(jīng)不在棧中了(block此時(shí)在哪里,?),再用指針訪問就……,。

所以,,對于auto類型的局部變量,不允許block進(jìn)行修改是合理的,。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多