Block是C级语法和运行时特性。它们相似于标准C函数,可是除了可执行代码以外,它们还可能包含对自动(堆栈)或托管(堆)内存的变量绑定。所以,Block能够维护一组状态(数据),它能够用来在执行时影响行为。c++
您可使用Blocks来组合函数表达式,这些表达式能够被传递给API,可选地存储,并由多个线程使用。Block对于回调来讲特别有用,由于块包含在回调上执行的代码和执行过程当中须要的数据。app
能够在GCC和Clang中使用OS X v10.6 Xcode开发工具。您可使用OS X v10.6和以后的模块,以及随后的iOS 4.0。Block运行时是开源的,能够在LLVM的编译器-rt子项目存储库中找到。block也被提交给C标准工做组做为N1370:苹果对C的扩展,由于Objective-C和c++都是从C派生出来的,block被设计用于与全部三种语言(以及Objective-C++)一块儿工做。语法反映了这个目标。iphone
__block
变量加了它为啥就能被修改了?不少人也许会说是传入了地址,这种说法很片面的,若是是这样的其实彻底不用加__block
,苹果帮你作了就好了呀。其实__block以后翻译成C语言源码以后,变量会变成一个对象,该对象存储了原来变量的地址,函数传入的是一个对象。经过LLVM的编译器-rt子项目存储库中找到runtime下的Block源码svn
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
复制代码
当咱们声明一个Block的时候,编译器其实会将block转换成以上struct结构体。 其中isa指向的是Block具体的类。有以下6中,不过其中StackBlock
、MallocBlock
、GlobalBlock
是比较常见的函数
/* the raw data space for runtime classes for blocks */
/* class+meta used for stack, malloc, and collectable based blocks */
BLOCK_EXPORT void * _NSConcreteStackBlock[32];
BLOCK_EXPORT void * _NSConcreteMallocBlock[32];
BLOCK_EXPORT void * _NSConcreteAutoBlock[32];
BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];
BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32];
复制代码
invoke函数指针则是对应Objective-C中代码的具体实现工具
看到这里不知你们有没有想到runtime中objc_object
的isa呢? 其实两个原理是同样的。这里就不具体介绍objc_object
开发工具
因此Block即为Objective-C的对象spa
Block的类型变量声明以下:线程
int (^blk) (int);翻译
咱们声明一个Block类型变量而且赋值
int (^blk) (int) = ^(int count){return count+1};
复制代码
而后咱们经过以下
xcrun -sdk iphonesimulator11.2 clang -rewrite-objc -F /Applications/Xcode\ 2.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/System/Library/Frameworks ViewController.m
咱们能够获得如下源码:
//对应的Block具体struct结构体
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//Block方法的具体实现
static int __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself, int count) {
return count+1;
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
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"));
int (*blk) (int) = ((int (*)(int))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 2);
}
复制代码
其中int (*blk) (int)
则就是对应咱们的Block类型变量,这里就就能够看到了Block类型变量的本质了,其实就是C语言的函数指针
说截获自动变量以前咱们先看如下代码
int tmp = 2;
int (^blk) (int) = ^(int count){
return count+tmp;
};
tmp = 3;
int result = blk(2);
NSLog(@"%d",result);
复制代码
以上代码会打印出多少呢?5仍是4?? 正确答案是4
为何是4呢?其实就是由于Block截获自动变量的缘由
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
int tmp;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _tmp, int flags=0) : tmp(_tmp) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself, int count) {
int tmp = __cself->tmp; // bound by copy
return count+tmp;
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
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"));
int tmp = 2;
int (*blk) (int) = ((int (*)(int))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, tmp));
tmp = 3;
int result = ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6n_mdf6rn0d5f5cw6r1h2620h3w0000gn_T_ViewController_b80e84_mi_0,result);
}`__ViewController__viewDidLoad_block_impl_0`
复制代码
经过如下代码能够看出,当咱们在给Block类型变量赋值的时候,tmp变量同时被传入,而且被保存到了__ViewController__viewDidLoad_block_impl_0
的struct中。这时候其实就是截获了自动变量,因为已经在struct类中保存了一份,即便后边更改,也不会影响Block截获的值。
int (*blk) (int) = ((int (*)(int))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, tmp));
复制代码
为何要对局部变量进行截获呢?而全局变量和静态变量不须要截获,而且修改的的时候也不须要加__block
呢?
主要缘由就是变量的生命周期。局部变量在代码块执行结束以后就会被释放,可是Block不必定在此时释放。因此就会出现变量超过生命周期的现象,此时对局部变量进行截获,即便局部变量被释放,可是Block一样仍是能够正常使用的。由于全局变量和静态变量的释放时间确定不会在Block以前,因此没必要对他们进行截获。
全局变量和静态变量存储在全局数据区;局部变量存储在栈中
不知道你们有没有想过一个问题,为何须要__block呢?若是没有__block难道就修改不了变量了吗?
咱们先看一下加了__block编译器给咱们作了啥
struct __Block_byref_tmpBlock_0 {
void *__isa;
__Block_byref_tmpBlock_0 *__forwarding;
int __flags;
int __size;
int tmpBlock;
};
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__Block_byref_tmpBlock_0 *tmpBlock; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_tmpBlock_0 *_tmpBlock, int flags=0) : tmpBlock(_tmpBlock->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself, int count) {
__Block_byref_tmpBlock_0 *tmpBlock = __cself->tmpBlock; // bound by ref
(tmpBlock->__forwarding->tmpBlock) = 100;
return count+(tmpBlock->__forwarding->tmpBlock);
}
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->tmpBlock, (void*)src->tmpBlock, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void*)src->tmpBlock, 8/*BLOCK_FIELD_IS_BYREF*/);}
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};
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_tmpBlock_0 tmpBlock = {(void*)0,(__Block_byref_tmpBlock_0 *)&tmpBlock, 0, sizeof(__Block_byref_tmpBlock_0), 2};
int (*blk) (int) = ((int (*)(int))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_tmpBlock_0 *)&tmpBlock, 570425344));
int result = ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6n_mdf6rn0d5f5cw6r1h2620h3w0000gn_T_ViewController_a1c583_mi_0,result);
}
复制代码
这时候你们能够与前边没有加__block
的代码进行比较,二者的差异在哪里。 其实你们应该很容易发现加了__block
以后,变量造成了一个struct,这个struct中保存了变量的值,同时还有一个__forwarding
。这个__forwarding
其实就是为何须要__block的关键。
Block从栈复制到堆的时候,__block变量也会受到影响。以下:
__block变量的配置存储域 | Block从栈到堆时的影响 |
---|---|
栈 | 从栈复制到堆并被Block持有 |
堆 | 被Block持有 |
ARC下,Block若是是栈的话,默认会copy到堆上。此时所使用的__block变量同时也会从栈被复制到堆上以下图
那若是Block在堆上了,咱们在Block中修改了变量,怎么让栈上的变量也同时能正确访问呢?这其实就是__forwarding
功劳了。
__block
变量初始化的时候__forwarding
是指向自己本身的。当__block
变量从栈复制到堆上的时候,此时会将__forwarding
的值替换为复制到目标堆上的__block
变量用结构体实例的地址。以下图:
经过此功能,不管是在Block语法中、Block语法外使用__block
变量,仍是__block
变量配置在栈上或堆上,均可以顺利地访问一个__block
变量。 到这里你们应该明白了"__block
加了以后,是把变量的地址传入Block"的说法是很片面的吧啦