成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS開發(fā):block的探究

移動開發(fā) iOS
一個函數(shù)里定義了個block,這個block可以訪問該函數(shù)的內(nèi)部變量。本文就來介紹一下iOS開發(fā)中的

​[0. Brief introduction of block]

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

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

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

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

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

一個簡單的Block示例如下:

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

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

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

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

如果用BNF來表示block的上下文無關文法,大致如下:

  1. block_expression  ::=  ^  block_declare  block_statement 
  2. block_declare  ::=  block_return_type  block_argument_list 
  3. block_return_type ::=  return_type  |  空 
  4. block_argument_list  ::=  argument_list  |  空

 


[1. Why block]

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

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

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

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

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

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

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

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

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

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


[2. About __block_impl]

Clang提供了中間代碼展示的選項供我們進一步了解block的原理。

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

使用-rewrite-objc選項編譯:

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

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

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

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

不過,block相關的變量放哪里呢?上面提到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個)進行命名的。如果是全局 block,就根據(jù)變量名和出現(xià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對應的函數(shù)體。該函數(shù)接受一個__cself參數(shù),即對應的block自身。

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

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

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

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

別走開,下頁為您精彩繼續(xù)

#p#


[4. Capture local variable]

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

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

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

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

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

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

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

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


[5. Modify static local variable]

于是我們也可以推斷出,靜態(tài)局部變量是如何在block執(zhí)行體中被修改的——通過指針。

因為靜態(tài)局部變量存在于數(shù)據(jù)段中,不存在棧展開后非法訪存的風險。

上面中間代碼片段與前一個片段的差別主要在于main函數(shù)里傳遞的是i的地址(&i),以及__main_block_impl_0結(jié)構(gòu)體中成員i變成指針類型(int *)。

然后在執(zhí)行block時,通過指針修改值。

當然,全局變量、靜態(tài)全局變量都可以在block執(zhí)行體內(nèi)被修改。更準確地講,block可以修改它被調(diào)用(這里是__main_block_func_0)時所處作用域內(nèi)的變量。比如一個block作為成員變量時,它也可以訪問同一個對象里的其它成員變量。


[6. Implementation of __block variable]

那么,__block類型變量是如何支持修改的呢?

 

我們?yōu)閕nt類型變量加上__block指示符,使得變量i可以在block函數(shù)體中被修改。

此時再看中間代碼,會多出很多信息。首先是__block變量對應的結(jié)構(gòu)體:

由***個成員__isa指針也可以知道__Block_byref_i_0也可以是NSObject。

第二個成員__forwarding指向自己,為什么要指向自己?指向自己是沒有意義的,只能說有時候需要指向另一個__Block_byref_i_0結(jié)構(gòu)。

***一個成員是目標存儲變量i。

此時,__main_block_impl_0結(jié)構(gòu)如下:

__main_block_impl_0的成員變量i變成了__Block_byref_i_0 *類型。

對應的函數(shù)__main_block_func_0如下:

亮點是__Block_byref_i_0指針類型變量i,通過其成員變量__forwarding指針來操作另一個成員變量。 :-)

而main函數(shù)如下:

通過這樣看起來有點復雜的改變,我們可以修改變量i的值。但是問題同樣存在:__Block_byref_i_0類型變量i仍然處于棧上,當block被回調(diào)執(zhí)行時,變量i所在的棧已經(jīng)被展開,怎么辦?

在這種關鍵時刻,__main_block_desc_0站出來了:

此時,__main_block_desc_0多了兩個成員函數(shù):copy和dispose,分別指向__main_block_copy_0和__main_block_dispose_0。

當block從棧上被copy到堆上時,會調(diào)用__main_block_copy_0將__block類型的成員變量i從棧上復制到堆上;而當block被釋放時,相應地會調(diào)用__main_block_dispose_0來釋放__block類型的成員變量i。

一會在棧上,一會在堆上,那如果棧上和堆上同時對該變量進行操作,怎么辦?

這時候,__forwarding的作用就體現(xiàn)出來了:當一個__block變量從棧上被復制到堆上時,棧上的那個__Block_byref_i_0結(jié)構(gòu)體中的__forwarding指針也會指向堆上的結(jié)構(gòu)。


本來還想繼續(xù)寫下去,結(jié)果發(fā)現(xiàn)文章有點長了。先到此。

責任編輯:閆佳明 來源: cocoachina
相關推薦

2017-02-15 09:25:36

iOS開發(fā)MQTT

2025-01-10 09:47:43

blockSDKiOS

2010-09-16 09:13:09

CSS display

2017-03-07 09:45:43

iOSBlock開發(fā)

2013-07-19 12:52:50

iOS中BlockiOS開發(fā)學習

2015-12-23 09:16:33

ios動畫渲染機制

2015-12-30 14:16:05

iOS動畫視圖渲染

2018-05-27 17:44:53

私有庫索引庫倉庫

2011-08-08 18:11:45

IOS 4Block UIActionShe

2010-09-28 15:38:23

Java ME

2024-08-28 08:00:00

2013-07-19 14:00:13

iOS中BlockiOS開發(fā)學習

2013-07-19 14:35:59

iOS中BlockiOS開發(fā)學習

2016-03-07 09:09:35

blockios開發(fā)實踐

2013-07-19 13:16:26

iOS中BlockiOS開發(fā)學習內(nèi)存管理

2010-08-02 16:51:54

2017-03-06 16:13:41

深度學習人工智能

2015-03-18 09:29:12

iOS開發(fā)爭議

2010-09-29 09:54:09

J2ME應用程序

2010-09-30 13:06:33

Myeclipse J
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 黄一区二区三区 | 日韩亚洲视频 | 国产精品福利久久久 | 亚卅毛片 | 影音先锋亚洲资源 | 亚洲日本欧美 | 狠狠草视频 | 视频一二区| 日韩一区二区三区四区五区 | av乱码 | 日本a网站 | 日韩一区二区三区视频在线播放 | 91精品麻豆日日躁夜夜躁 | 奇色影视 | 亚洲区一区二 | 亚洲精品一区二区久 | 成人av播放 | 亚洲精品免费在线 | 伊人狼人影院 | 中文字幕在线精品 | 成人在线免费观看 | www国产精品 | 国产一区二区三区视频 | 国产精品久久久爽爽爽麻豆色哟哟 | 一区二区在线免费观看 | 四虎影院一区二区 | 中文字幕精品一区 | 日韩免费一区二区 | 中文字幕1区 | www.亚洲 | 亚洲高清视频一区二区 | 国产区在线| 国产免费观看久久黄av片涩av | 一区二区三区欧美在线 | 欧美久久久| 成人自拍av | 国产高清视频一区 | 久久久久久国产精品mv | 国产日韩精品在线 | 一级免费在线视频 | 久久久久久久久国产成人免费 |