- Block是帶有自動變量值的匿名函數(shù)梳庆;
-
帶有自動變量值
在Block中表現(xiàn)為截獲自動變量值
膏执; - 自動變量值截獲只能保存執(zhí)行Block語法瞬間的值露久,保存后就不能改寫該值毫痕;
- 向截獲的自動變量賦值會產(chǎn)生編譯錯誤消请,但使用截獲的值不會產(chǎn)生任何問題;
- 使用
__block
說明符的自動變量可在Block中賦值蛉加,該變量稱為__block
變量针饥; - Block其實就是OC對象丁眼;
一贺辰、Block的實質(zhì)
使用 clang -rewrite-objc main.m
將包含Block語法的源代碼轉(zhuǎn)換成C++的源代碼饲化。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
void (^blk)(void) = ^{
printf("-------------Block------------------\n");
};
blk();
return 0;
}
轉(zhuǎn)換后的C++源碼:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; //指向Block的實現(xiàn)
};
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------------------\n");
}
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, const 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;
}
源碼中的Block語法:
^{ printf("-------------Block------------------\n"); };
變換后的源代碼中也含有相同的表達式:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("-------------Block------------------\n");
}
參數(shù)__cself
是指向__main_block_impl_0
結(jié)構(gòu)體的指針變量硫眨。
struct __main_block_impl_0 *__cself;
該結(jié)構(gòu)體的聲明如下:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
};
第一個成員變量是impl
巢块,其結(jié)構(gòu)體如下:
struct __block_impl {
void *isa;
int Flags; // 標志
int Reserved; // 預(yù)留區(qū)
void *FuncPtr; // 函數(shù)指針
};
第二個成員變量是Desc
指針族奢,其__main_block_desc_0
結(jié)構(gòu)體如下:
static struct __main_block_desc_0 {
size_t reserved; // 預(yù)留區(qū)
size_t Block_size; // Block大小
} ;
下面是包含這些結(jié)構(gòu)體的__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;
}
下面是構(gòu)造函數(shù)的調(diào)用:
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
將上面的轉(zhuǎn)換部分刪除之后棚品,具體如下:
struct __main_block_impl_0 temp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
struct __main_block_impl_0 *blk = &temp;
Block的調(diào)用轉(zhuǎn)化為以下源碼:
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
將上面的轉(zhuǎn)換部分刪除之后铜跑,具體如下:
(*blk->impl.FuncPtr)(blk);
這就是簡單的使用函數(shù)指針調(diào)用函數(shù)骡澈。由Block語法轉(zhuǎn)換的__main_block_func_0
函數(shù)的指針被賦值成員變量FuncPtr
中肋殴。此外囤锉,__main_block_func_0
函數(shù)的參數(shù)
__cself
指向Block值。在調(diào)用該函數(shù)的源碼中可以看出Block正是作為參數(shù)進行了傳遞护锤。
成員變量isa
的初始化如下:
impl.isa = &_NSConcreteStackBlock;
_NSConcreteStackBlock
相當于class_t
結(jié)構(gòu)體實例嚼锄。將Block作為OC對象處理時,關(guān)于該類的信息位于_NSConcreteStackBlock
中蔽豺。
Block的實質(zhì)就是Block為Objective-C對象区丑。
二、捕獲自動變量值
如下示例:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
int dmy = 250;
int val = 10;
const char * fmt = "val = %d \n";
void (^blk)(void) = ^{
printf(fmt,val);
};
blk();
return 0;
}
Block捕獲局部變量val
和fmt
的值修陡,轉(zhuǎn)換之后的源碼如下:
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 copy
int val = __cself->val; // bound by copy
printf(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, const char * argv[]) {
int dmy = 250;
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));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
由上面的源碼可知沧侥,Block語法表達式中使用的自動變量被作為成員變量追加到__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;
};
下面來看Block匿名函數(shù)的實現(xiàn),初始源碼如下:
void (^blk)(void) = ^{
printf(fmt,val);
};
該源碼轉(zhuǎn)換為以下函數(shù):
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt; // bound by copy
int val = __cself->val; // bound by copy
printf(fmt,val);
}
由上可知:所謂截獲自動變量值
意味在執(zhí)行Block語法時拾因,Block語法表達式所使用的自動變量值被保存到Block的結(jié)構(gòu)體實例(即Block自身)中旺罢。
三旷余、全局變量、全局靜態(tài)變量和局部靜態(tài)變量
#import <Foundation/Foundation.h>
// 全局變量
int global_val = 1990;
// 全局靜態(tài)變量
static int static_global_val = 1011;
int main(int argc, const char * argv[]) {
// 局部靜態(tài)變量
static int static_val = 1994;
void (^blk)(void) = ^{
global_val *= 2;
static_global_val *= 3;
static_val *= 4;
};
blk();
return 0;
}
在上面的示例中扁达,定義了三個變量正卧,一個全局變量global_val
,一個靜態(tài)全局變量 static_global_val
跪解,一個局部靜態(tài)變量static_val
炉旷,然后在Block語法中修改這三個變量的值。該源碼轉(zhuǎn)換后如下:
int global_val = 1990;
static int static_global_val = 1011;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_val;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_val = __cself->static_val; // bound by copy
global_val *= 2;
static_global_val *= 3;
(*static_val) *= 4;
}
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, const char * argv[]) {
static int static_val = 1994;
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
對全局變量global_val
和靜態(tài)全局變量 static_global_val
的訪問和轉(zhuǎn)換前完全相同叉讥。而對局部靜態(tài)變量static_val
的訪問轉(zhuǎn)換如下:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_val = __cself->static_val; // bound by copy
(*static_val) *= 4;
}
使用局部靜態(tài)變量static_val
的指針對其進行訪問窘行,將static_val
的指針傳遞給__main_block_impl_0
結(jié)構(gòu)體的構(gòu)造函數(shù)并保存。
四图仓、__block
存儲域類說明符
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
__block int val = 1994;
void (^blk)(void) = ^{
val += 25;
};
blk();
return 0;
}
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) += 25;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 1994};
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
由上面的源碼可知罐盔,__block變量被轉(zhuǎn)換成__Block_byref_val_0
結(jié)構(gòu)體類型的自動變量,即棧上生成的__Block_byref_val_0
結(jié)構(gòu)體實例救崔。該結(jié)構(gòu)體聲明如下:
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
下面是給__block變量賦值的源碼轉(zhuǎn)換:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) += 25;
}
在給Block中的局部靜態(tài)變量賦值時翘骂,使用了指向該靜態(tài)變量的指針。而向 __block變量賦值則更復(fù)雜帚豪。Block的__main_block_impl_0
結(jié)構(gòu)體實例持有指向 __block變量的__Block_byref_val_0
結(jié)構(gòu)體實例的指針碳竟。
__Block_byref_val_0
結(jié)構(gòu)體實例的成員變量__forwarding
持有指向該實例自身的指針。通過__forwarding
訪問成員變量val狸臣,可以保證 __block變量無論配置在棧上還是堆上都可以正確的訪問__block變量莹桅。這是因為 __block變量的實例在棧上時,__forwarding
是指向自身的指針烛亦,當 __block變量從棧上拷貝到堆上時诈泼,__forwarding
將指向復(fù)制到堆上的__block變量實例。
__block變量的__Block_byref_val_0
結(jié)構(gòu)體并不在Block的__main_block_impl_0
結(jié)構(gòu)體中煤禽,是為了在多個Block中使用 __block變量铐达。