系列文章:
iOS Block概念、語法及基本使用
iOS Block實現(xiàn)原理
iOS Block存儲域及循環(huán)引用
上一節(jié)我們已經明白Block的實質了,語法奇特蚂会,最終也是轉換為C語言來處理的园细。那么這一節(jié)來看看 __block修飾符到底干了啥,為什么使用了 __block修飾符昔馋,在Block內部就可以改變變量值了呢筹吐,帶著疑問來看下編譯后的源碼。
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int val = 10;
void (^blk)(void) = ^{
val = 2;
printf("%d\n",val);
};
blk();
}
return 0;
}
編譯后绒极,
一步一步來看:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
上述__block_impl結構體沒有變化骏令。
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
};
Desc結構體多了兩個函數(shù),copy dispose垄提,貌似從字面上可以猜測到是用來干嘛的榔袋,持有和銷毀周拐。
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*/);}
這兩個函數(shù)貌似不就是對應__main_block_desc_0結構體copy和dispose函數(shù)嘛,現(xiàn)在只是猜想凰兑,接著往下看妥粟。
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;
}
};
來看看Block變量編譯后的Block結構體。多了 __Block_byref_val_0 *val結構體指針變量吏够,這個東西是什么呢勾给,帶有val,val不是我們的變量名稱嗎(int val = 10)锅知,構造函數(shù)參數(shù)也有相應增加播急。
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
臥槽看到了吧,原來把我們的變量轉成了結構體售睹。__block int val = 10;這是源代碼桩警,如果不加
__block說明符,val會變成__main_block_impl_0的一個變量(上節(jié)我們已經得知)昌妹,加上了__block說明符后捶枢,這個變量就變成了結構體了,神不神奇飞崖。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val; // bound by ref 上下文拿到__cself烂叔,也就是__main_block_impl_0結構體實例
(val->__forwarding->val) = 2;
printf("%d\n",(val->__forwarding->val));
}
該函數(shù)賦值給結構體__block_impl內的FuncPtr函數(shù)指針。
int main(int argc, const char * argv[]) {
/* @autoreleasepool*/ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {//初始化
(void*)0,
(__Block_byref_val_0 *)&val,
0,
sizeof(__Block_byref_val_0),
10
};
//block結構體實例初始化及賦值
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
//block調用
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}
來看下main函數(shù)固歪,把__block修飾的變量初始化 為_Block_byref_val_0結構體變量蒜鸡,并把默認值10帶進去。Block的__main_block_impl_0結構體實例持有指向 __block 變量的__Block_byref_val_0結構體實例的指針昼牛。
__Block_byref_val_0結構體實例的成員變量_forwarding指針指向自己术瓮。通過__forwarding訪問成員變量val(成員變量val是該實例自己持有的變量,它相當于原自動變量贰健,也就是 int val= 10)胞四。
下面看看 single APP 使用__block說明符編譯后的源代碼:
源代碼
- (void)viewDidLoad {
[super viewDidLoad];
__block NSArray *arr = nil;
void (^blk)(void) = ^{
arr = [NSArray array];
};
blk();
}
編譯后:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = {
0,
sizeof(struct __ViewController__viewDidLoad_block_impl_0),
__ViewController__viewDidLoad_block_copy_0,
__ViewController__viewDidLoad_block_dispose_0
};
//變量結構體
struct __Block_byref_arr_0 {
void *__isa;
__Block_byref_arr_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSArray *arr;
};
//賦值給__block_impl結構體實例內的FuncPtr函數(shù)指針
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
__Block_byref_arr_0 *arr = __cself->arr; // bound by ref
(arr->__forwarding->arr) = ((NSArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
}
//copy dispose
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_assign((void*)&dst->arr, (void*)src->arr, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void*)src->arr, 8/*BLOCK_FIELD_IS_BYREF*/);}
//block結構體實例
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__Block_byref_arr_0 *arr; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_arr_0 *_arr, int flags=0) : arr(_arr->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//viewDidLoad函數(shù)(后邊為OC語言的默認參數(shù):id self, SEL _cmd)
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
__attribute__((__blocks__(byref))) __Block_byref_arr_0 arr = {
(void*)0,
(__Block_byref_arr_0 *)&arr,
33554432,
sizeof(__Block_byref_arr_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
__null
};
//block結構體實例初始化并賦值
void (*blk)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_arr_0 *)&arr, 570425344));
//block調用
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
以上就是__block說明符(具體說是存儲域類說明符)所做的事情。
當然肯定會有疑問的伶椿,copy dispose 函數(shù)指針只是提了一下辜伟,它們到底做了哪些事情沒有具體講,下節(jié)會講到脊另。
- Block的種類及存儲域
- __block變量存儲域
- Block循環(huán)引用