国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

iOS開發:block的探究

移動開發 iOS
一個函數里定義了個block,這個block可以訪問該函數的內部變量。本文就來介紹一下iOS開發中的

​[0. Brief introduction of block]

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

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

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

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

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

一個簡單的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定義的函數體中,可以很自然地再用def語句定義內嵌函數,因為這些函數本質上都是對象。

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

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

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

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

其中將stop設為YES,就跳出循環,不繼續遍歷了。

而在很多框架中,block越來越經常被用作回調函數,取代傳統的回調方式。

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

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

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

任務完成時回調
處理消息監聽回調處理
錯誤回調處理
枚舉回調
視圖動畫、變換
排序


[2. About __block_impl]

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

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

使用-rewrite-objc選項編譯:

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

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

從結構體的成員可以看出,Flags、Reserved可以先略過,isa指針表明了block可以是一個NSObject,而FuncPtr指針顯然是block對應的函數指針。

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

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


[3. Implementation of a simple block]

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

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

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

接著出現的是__main_block_func_0函數,即block對應的函數體。該函數接受一個__cself參數,即對應的block自身。

再下面是__main_block_desc_0結構體,其中比較有價值的信息是block大小。

***就是main函數中對block的創建和調用,可以看出執行block就是調用一個以block自身作為參數的函數,這個函數對應著block的執行體。

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

由 于block也是NSObject,我們可以對其進行retain操作。不過在將block作為回調函數傳遞給底層框架時,底層框架需要對其copy一 份。比方說,如果將回調block作為屬性,不能用retain,而要用copy。我們通常會將block寫在棧中,而需要回調時,往往回調block已 經不在棧中了,使用copy屬性可以將block放到堆中。或者使用Block_copy()和Block_release()。

別走開,下頁為您精彩繼續

#p#


[4. Capture local variable]

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

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

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

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

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

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

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

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


[5. Modify static local variable]

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

因為靜態局部變量存在于數據段中,不存在棧展開后非法訪存的風險。

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

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

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


[6. Implementation of __block variable]

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

 

我們為int類型變量加上__block指示符,使得變量i可以在block函數體中被修改。

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

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

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

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

此時,__main_block_impl_0結構如下:

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

對應的函數__main_block_func_0如下:

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

而main函數如下:

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

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

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

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

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

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


本來還想繼續寫下去,結果發現文章有點長了。先到此。

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

2025-01-10 09:47:43

blockSDKiOS

2017-02-15 09:25:36

iOS開發MQTT

2010-09-16 09:13:09

CSS display

2017-03-07 09:45:43

iOSBlock開發

2013-07-19 12:52:50

iOS中BlockiOS開發學習

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

2016-03-07 09:09:35

blockios開發實踐

2013-07-19 14:35:59

iOS中BlockiOS開發學習

2013-07-19 14:00:13

iOS中BlockiOS開發學習

2024-08-28 08:00:00

2013-07-19 13:16:26

iOS中BlockiOS開發學習內存管理

2010-08-02 16:51:54

2017-03-06 16:13:41

深度學習人工智能

2015-03-18 09:29:12

iOS開發爭議

2010-09-30 13:06:33

Myeclipse J

2010-09-29 09:54:09

J2ME應用程序
點贊
收藏

51CTO技術棧公眾號

日韩精品999| 国产专区在线视频| 白白色在线发布| 9久久9毛片又大又硬又粗| 爱情电影社保片一区| 成人午夜看片网址| 日本高清久久天堂| 成人动漫av| 国产一区二区剧情av在线| 成人黄色大片在线免费观看| 99伊人久久| 99久久99久久精品国产片果冰| 亚洲福利在线视频| 四季久久免费一区二区三区四区| 亚洲无线码一区二区三区| 欧美大片免费播放| 不卡av免费在线| 成人在线综合网| 日韩av高清| 日韩一区精品字幕| 成人资源av| 成人情趣视频网站| 蜜臀a∨国产成人精品| 国产福利片一区二区| 国内精品免费在线观看| 动漫av网站免费观看| 亚洲综合专区| 亚洲第一级黄色片| 1769视频在线播放免费观看| 精品肉辣文txt下载| 欧美日韩免费一区| 欧美三级理伦电影| 深夜成人在线观看| 国产 日韩 欧美 综合 一区| 97视频在线免费观看| 狠狠一区二区三区| 日韩影视高清在线观看| 亚洲最大福利网| 亚洲欧美大片| 欧美精品一区免费| 亚洲三级电影网站| 国内精品在线一区| 林ゆな中文字幕一区二区| 国产精品最新在线观看| 日韩精品久久久久久久电影99爱| 欧美最大成人综合网| 99re66热这里只有精品3直播| 轻点好疼好大好爽视频| 一区二区三区高清| 国产精品xxx视频| 国产一区二区三区免费看| 999精品视频在线| 在线观看亚洲精品视频| 厕沟全景美女厕沟精品| 亚洲天堂av高清| 97se亚洲综合在线| 国产精品久久久久久影院8一贰佰 国产精品久久久久久麻豆一区软件 | 日韩中文字幕亚洲一区二区va在线 | 色黄视频在线观看| 久久影视电视剧免费网站清宫辞电视| 影视亚洲一区二区三区| 少妇无码av无码专区在线观看 | 久久久影院一区二区三区| 国产成人av在线影院| 免费在线超碰| 九九热最新视频//这里只有精品| 四虎一区二区| 日韩中文av| 日韩欧美在线免费观看视频| 日韩电影在线观看中文字幕 | 日本免费在线一区| 亚洲欧美在线网| 国产剧情av麻豆香蕉精品| 2022亚洲天堂| 在线影视一区二区三区| 亚洲一区二区电影| 男人女人黄一级| 欧美日本高清一区| 美腿丝袜亚洲一区| 欧美成年黄网站色视频| 国产精品一区二区三| 亚洲永久精品大片| 一区二区三区在线观看免费| 激情丁香在线| 国产日韩在线精品av| 亚洲一区在线视频观看| 欧美 日韩 国产一区二区在线视频 | 国产精品一国产精品最新章节| 久久国产剧场电影| 一区二区三区视频国产日韩| 丁香婷婷久久久综合精品国产| 色哟哟在线观看一区二区三区| 波多野结衣一区| 九九热免费精品视频| 久久久久久香蕉网| 欧美视频中文字幕| a在线欧美一区| 综合久草视频| 蜜臀精品一区二区| 搡老女人一区二区三区视频tv | 另类小说第一页| 91精品网站| 欧美日韩高清不卡| 91麻豆福利精品推荐| 欧美日韩中出| 欧美美女色图| 亚洲午夜精品久久久久久人妖| 精品免费视频.| 麻豆国产一区二区| 男男gaygays亚洲| 新呦u视频一区二区| 最近的2019中文字幕免费一页| 国产精品欧美一级免费| 免费在线看成人av| 成人中文视频| 久久久久久青草| 日韩不卡av| 国产精品白嫩美女在线观看 | 国产伦精品一区二区三区照片| 久久精品国产视频| 中文字幕中文乱码欧美一区二区 | 福利视频一区二区三区四区| 亚洲一区二区中文| 午夜精品在线视频| 日韩欧美久久一区| 亚洲摸摸操操av| 日本伊人色综合网| 综合精品久久| 日本中文在线| 色视频免费在线观看| 尤蜜粉嫩av国产一区二区三区| 一区二区三区久久精品| 中文字幕精品一区二区精品绿巨人| 在线成人av| 欧美成免费一区二区视频| 黄色精品视频| 尤物yw193can在线观看| wwwww在线观看免费视频| 秋霞福利视频| 黄色aaa级片| 乱小说综合网站| 色爽爽爽爽爽爽爽爽| 久久精品国产精品国产精品污 | 亚洲国产精品久久久久久| 欧美系列日韩一区| 欧美在线观看你懂的| 欧美日韩国产片| 精品久久国产老人久久综合| 欧美一二三在线| 欧美性高清videossexo| 欧美电影一区二区| 666欧美在线视频| 亚洲欧美日韩精品| 久久婷婷国产麻豆91天堂| 萌白酱国产一区二区| 国产精品稀缺呦系列在线| 成人性生交大片免费看视频直播| 狠狠色噜噜狠狠色综合久| 日韩在线第一区| 欧美在线观看成人| 国产免费黄色一级片| 最新中文字幕在线观看| 邻居大乳一区二区三区| 嫩草嫩草嫩草| 五月婷婷视频在线观看| 日韩成人18| 久久中文欧美| 亚洲日本在线a| 欧美日韩国产大片| 欧美激情视频网站| 欧美做爰性生交视频| 欧亚精品在线观看| 亚洲草草视频| 男男互摸gay网站| 日本高清中文字幕在线| 涩涩视频在线免费看| 午夜成年人在线免费视频| 成人春色在线观看免费网站| 久久国产精品亚洲人一区二区三区| 老牛国产精品一区的观看方式| 国产午夜精品一区二区 | 1024欧美极品| 91九色在线播放| 久久久久美女| 国产成人精品亚洲午夜麻豆| 亚洲大片一区二区三区| 日韩精品在线视频| 国产欧美精品一区二区| 一女二男3p波多野结衣| 九色porny视频在线观看| 久久高清免费| 国产99久久久久久免费看农村| 欧美日韩精品系列| 成人深夜直播免费观看| 成年人免费视频观看| 欧美性xxx| 91亚洲男人天堂| 精品国产污污免费网站入口 | 91骚色在线| 日韩理论电影中文字幕|