研究工具:clang
创建一个名为block.c的源文件
#include <stdio.h>
int main(){
^{printf("Hello ,World!\n");}();
return 0;
}
命令行输入:clang -rewrite-objc block.c 可在block.c所在的目录下看到一个block.cpp文件进行分析
block的主要构成
isa指针:所有对象都有该指针,用于实现对象的相关功能
invoke:函数指针,指向具体的block实现的函数指针
descriptor:标示该block的附加信息,主要是size大小,以及copy和dispose函数的指针
variables:block能够访问它外部的局部变量,就是因为这些变量(或者变量的地址)复制到了结构体中
结构体本身并不附带任何额外信息
创建block时,实际就是在方法中声明一个struct,并且初始化该struct的成员。而执行block时,就是调用那个单独的C函数,并把该struct指针传递过去。
在objc中,根据对象的定义,凡是首地址是*isa的结构体指针,都可以认为是对象(id).
常见的3种类型的block
_NSConcreteGlobalBlock,全局静态block,不会访问任何外部变量
_NSConcreteStackBlock,保存在栈中的block,block返回时被销毁
_NSConcreteMallocBlock,保存在堆中的block,引用计数为0时被销毁
只有一个block被调用copy方法的时候,系统才会将这个block复制到堆上,从而产生NSConcreteMallocBlock类型的block
局部变量在block内不能被修改
static void__main_block_func_0(struct__main_block_impl_0 *__cself) {
inti = __cself->i;// bound by copy
printf("%d",i);}
在block中引用的局部变量i,实际上是在声明block时,被复制到__main_block_impl_0结构体的那个变量,block内部修改变量i,不会影响到外部的变量i.
__block修改局部变量
struct__Block_byref_i_0 {
void*__isa; //凡是首地址是*isa的结构体指针,都可以认为是对象(id).
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;}
变量i,成了一个结构体 在__main_block_impl_0 中引用着__Block_byref_i_0 *i;// by ref 结构体指针 ,这样就起到修改外部局部变量的作用
负责结构体__Block_byref_i_0的内存管理,增加了copy和dispose函数指针,用于在调用前后修改相应变量的引用计数
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};
ARC对block类型的影响
int main(int argc,char* argv[]) {
@autoreleasepool{
int i =1024;
void(^block)(void) = ^{
printf("%d",i);
};
block();
NSLog(@"%@",block);
return0;}
}
非ARC运行结果//10242016-06-14 10:46:03.238 MemoryInMRC[7473:3171916] <__NSStackBlock__: 0x7fff5a58f548>
ARC运行结果//10242016-06-14 10:49:03.121 MemoryInMRC[7528:3174176] <__NSMallocBlock__: 0x7fe36a7003e0>
循环引用
self对block引用,block捕捉到self时
XYZBlockKeeper*__weakweakSelf=self; self.block=^{ [weakSelf doSomething];//捕获到的是弱引用 }
如果捕获到的是当前对象的成员变量对象,同样也会造成对self的引用
id tmpIvar=_ivar;//临时变量,避免了self引用 self.block=^{ [tmpIvar msg]; }
block对变量的捕获规则
静态储存区变量:全局变量、static修饰变量可以修改
block接受的参数,可以修改
__block引用,可以修改。//bound by reference 如果时id类型则不会被block retain,必须手动处理其内存管理。
局部变量不可以修改//bound by copy