Objective-C 之Block(2)
Block的實質(zhì)
Block是“帶有自動變量值的匿名函數(shù)”。
通過clang -rewirte-objc 源代碼文件名就能將含有Block語法的源代碼變換為cpp的源代碼。
int main(int argc, char * argv[]) {void (^blk)(void) = ^{printf("Block");};blk();return 0;} 復(fù)制代碼變換后截取其中的代碼邏輯部分如下:
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Block");}static struct __main_block_desc_0 {size_t reserved;size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, char * argv[]) {void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;}復(fù)制代碼首先為:
^{printf("Block");}; 復(fù)制代碼變換后為:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Block"); } 復(fù)制代碼通過Block使用的匿名函數(shù)實際上被作為簡單的C語言函數(shù)來處理。 另外,根據(jù)Blocl語法所屬的函數(shù)名(此處為main)和該Block語法在函數(shù)出現(xiàn)的順序值(此處為0)來給經(jīng)clang變換的函數(shù)命名。 該函數(shù)的參數(shù)__cself相當于C++實例方法中志向?qū)嵗陨淼淖兞縯his,或者OC中的self,即參數(shù)__cself為志向Block值的變量。
這個方法中參數(shù)的聲明為:
struct __main_block_impl_0 *__cself 復(fù)制代碼參數(shù)__cself是__main_block_impl_0結(jié)構(gòu)體的指針。
該結(jié)構(gòu)體聲明如下:
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼去除構(gòu)造函數(shù)的部分為:
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc; };復(fù)制代碼再看__block_impl結(jié)構(gòu)體的聲明:
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; }; 復(fù)制代碼里面包含某些標志、今后版本升級所需的區(qū)域以及函數(shù)指針。 第二個成員變量是Desc指針,以下為__main_block_desc_0結(jié)構(gòu)體的聲明。
static struct __main_block_desc_0 {size_t reserved;size_t Block_size; } 復(fù)制代碼其結(jié)構(gòu)為今后版本升級所需要的區(qū)域和Block的大小。
再看__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù):
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} 復(fù)制代碼以上就是初始化__main_block_impl_0結(jié)構(gòu)體成員的源代碼。
再看看main函數(shù)中構(gòu)造函數(shù)的調(diào)用如下:
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); 復(fù)制代碼去掉轉(zhuǎn)換部分來看如下:
__main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0,&__main_block_imp_0_DATA);struct __main_block_impl_0 *blk = &temp; 復(fù)制代碼該源代碼將__main_block_impl_0結(jié)構(gòu)體的自動變量,即棧上生成的__main_block_impl_0結(jié)構(gòu)體實例的指針,賦值給__main_block_impl_0結(jié)構(gòu)體指針類型的變量blk。
以下這句代碼代表最初的打印代碼:
void (^blk)(void) = ^{printf("Block");}; 復(fù)制代碼將打印的Block塊賦給Block類型變量blk,相當于將__main_block_impl_0結(jié)構(gòu)體的指針賦值給變量blk。打印的代碼塊就是__main_block_impl_0結(jié)構(gòu)體類型的自動變量,即棧上生成的__main_block_impl_0的結(jié)構(gòu)體實例。
再來看__main_block_impl_0實例的構(gòu)造方法參數(shù):
__main_block_impl_0(__main_block_func_0,&__main_block_imp_0_DATA); 復(fù)制代碼第一個參數(shù)是由Block語法轉(zhuǎn)換C語言函數(shù)指針。第二個參數(shù)是作為靜態(tài)全局變量初始化的__main_block_desc_0結(jié)構(gòu)體實例指針。
下面是__main_block_desc_0結(jié)構(gòu)體實例的初始化部分代碼。
static struct __main_block_desc_0 {size_t reserved;size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; 復(fù)制代碼由上部分代碼可以,該源代碼使用Block,即__main_block_impl_0結(jié)構(gòu)體實例的大小,進行初始化。
再來看__main_block_impl_0結(jié)構(gòu)體
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };復(fù)制代碼以上,__main_block_impl_0結(jié)構(gòu)體等同于,結(jié)構(gòu)體構(gòu)造函數(shù)會如下進行初始化:
struct __main_block_impl_0 {void *isa;int Flags;int Reserved;void *FuncPtr;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼那么調(diào)用打印代碼塊的部分應(yīng)該為blk();
就可以變換為以下代碼:
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);復(fù)制代碼去掉轉(zhuǎn)換部分后:
(*blk->impl.FuncPtr)(blk); 復(fù)制代碼這是簡單地使用函數(shù)指針調(diào)用函數(shù)。由打印代碼塊轉(zhuǎn)換的__main_block_func_0函數(shù)的指針被賦值到結(jié)構(gòu)體的成員變量FuncPtr中,也說明了__main_block_func_0的參數(shù)__cself指向Block值。
但是impl.isa = &_NSConcreteStackBlock;,將Block指針賦值給Block結(jié)構(gòu)體成員變量isa。
注:isa為何物?
引用簡書作者曲年_《Objective-C isa 指針 與 runtime 機制》_一文中解釋如下
在Objective-C中,任何類的定義都是對象。類和類的實例(對象)沒有任何本質(zhì)上的區(qū)別。任何對象都有isa指針。
那么什么是類呢?在xcode中用快捷鍵Shift+Cmd+O 打開文件objc.h 能看到類的定義:
#if !OBJC_TYPES_DEFINED /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class;/// Represents an instance of a class. struct objc_object {Class _Nonnull isa OBJC_ISA_AVAILABILITY; };/// A pointer to an instance of a class. typedef struct objc_object *id; #endif/// An opaque type that represents a method selector. typedef struct objc_selector *SEL; 復(fù)制代碼可以看出: Class是一個objc_class結(jié)構(gòu)類型的指針,id是一個objc_object結(jié)構(gòu)類型的指針。
objc_class結(jié)構(gòu)體的定義如下:
struct objc_class {Class _Nonnull isa OBJC_ISA_AVAILABILITY;#if !__OBJC2__Class _Nullable super_class OBJC2_UNAVAILABLE;const char * _Nonnull name OBJC2_UNAVAILABLE;long version OBJC2_UNAVAILABLE;long info OBJC2_UNAVAILABLE;long instance_size OBJC2_UNAVAILABLE;struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif} OBJC2_UNAVAILABLE; /* Use `Class` instead of `struct objc_class *` */復(fù)制代碼各參數(shù)含義如下:
- isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念?#xff0c;而Class里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當類方法被調(diào)用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。
- super_class:父類,如果該類已經(jīng)是最頂層的根類,那么它為NULL。
- version:類的版本信息,默認為0
- info:供運行期使用的一些位標識。
- instance_size:該類的實例變量大小
- ivars:成員變量的數(shù)組
每一個對象本質(zhì)上都是一個類的實例。其中類定義了成員變量和成員方法的列表。對象通過對象的isa指針指向類。
每一個類本質(zhì)上都是一個對象,類其實是元類(meteClass)的實例。元類定義了類方法的列表。類通過類的isa指針指向元類。
所有的元類最終繼承一個根元類,根元類isa指針指向本身,形成一個封閉的內(nèi)循環(huán)。
objc_class結(jié)構(gòu)體與objc_object結(jié)構(gòu)體相同。但是,objc_object結(jié)構(gòu)題是各個對象在實現(xiàn)中使用的最基本的結(jié)構(gòu)體,objc_class是類在視線中使用的最基本的結(jié)構(gòu)體。
例:
@interface MyObject:NSObject {int val0;int val1; } 復(fù)制代碼基于objc_object結(jié)構(gòu)體,該類的對象的結(jié)構(gòu)體如下:
struct MyObject{Class isa;int val0;int val1; }復(fù)制代碼MyObject類的實例變量val0和val1被直接聲明為對象的結(jié)構(gòu)體成員。 OC中由各類生成對象意味著,像該結(jié)構(gòu)體這樣“生成由該類生成的對象的結(jié)構(gòu)體實例”。生成的各個對象,即由該類生成的對象的各個結(jié)構(gòu)體實例,通過成員變量isa保持該類的結(jié)構(gòu)體實例指針。
截獲自動變量值
int main(int argc, char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (^blk)(void)=^{printf(fmt,val);};val = 2;fmt = "These values were changed.val=%d\n";blk();return 0; } 復(fù)制代碼通過clang -rewrite-objc轉(zhuǎn)換后的代碼如下:
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;int val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);}static struct __main_block_desc_0 {size_t reserved;size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main(int argc, char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));val = 2;fmt = "These values were changed.val=%d\n";((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;}復(fù)制代碼有區(qū)別的部分僅僅在于以下部分:將變量作為成員變量追加到了__main_block_impl_0 結(jié)構(gòu)體中。
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;int val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼__main_block_impl_0結(jié)構(gòu)體內(nèi)聲明的成員變量了行于自動變量類型完全相同。但是Block語法表達式中沒有使用的自動變量不會被追加。Blocks的自動變量截獲只針對Block中使用的自動變量。
在初始化結(jié)構(gòu)體實例是,根據(jù)傳遞給構(gòu)造函數(shù)的參數(shù)對由自動變量追加的成員變量進行初始化。
__main_block_impl_0初始化的代碼過程總結(jié)來說就是以下賦值過程:
impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = &__main_block_desc_0_DATA; val = 10; fmt = "val = %d\n";復(fù)制代碼由此可知,在__main_block_impl_0結(jié)構(gòu)體實例中(即打印代碼塊),自動變量被截獲。
再來看其中匿名函數(shù)代碼塊:
^{printf(fmt,val); };復(fù)制代碼可以轉(zhuǎn)換為:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);} 復(fù)制代碼在轉(zhuǎn)換后的代碼中,截獲到__main_block_impl_0結(jié)構(gòu)體實例的成員變量上的自動變量,這些變量在Block語法表達式之前被聲明定義。因此,原來的源代碼表達式無需改動遍可以使用截獲的自動變量值執(zhí)行。
總的來說,所謂“截獲自動變量值”意味著在執(zhí)行Block語法時,Block語法表達式所使用的自動變量值被保存到Block的結(jié)構(gòu)實例中。
總結(jié)
以上是生活随笔為你收集整理的Objective-C 之Block(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分组加密的四种模式
- 下一篇: jdk紧急漏洞,XMLDecoder反序