//Block语法
//^返回值类型(参数列表){表达式}
- (void)some{
^int(int count){
return count + 1;
};
//返回值类型可省略
^(int count){
return count + 1;
};
//参数列表为空,可省略
^{
return @"123";
};
//最简洁的语法:^{};
}
- (block)someActionTwo:(block)block{
return^(inti){
block(2);
returni +1;
};
}
blockblockt = [selfsomeActionTwo:^int(inti) {
NSLog(@"##########i");
returni;
}];
block的用法
1.block作为参数
[selfsomeAction:^int(inti) {
NSLog(@"i");
returni ++;
}];
- (void)someAction:(int(^)(inti))block{
block(2);
}
2.block作为属性变量
//Block类型变量 语法:返回值类型(^变量名)(参数列表)
@property(nonatomic,copy)NSString *(^Block)(NSString *strone);
//借助typedef可简写typedef int(^block)(int);
block的分类
block分为栈block:_NSConcreteStackBlock,堆block:_NSConcreteMallocBlock,全局block:_NSConcreteGlobalBlock
栈block:block最初是在栈上创建的,只用到局部变量,没有被强指针指向的block是栈block;
堆block:在ARC中,系统会视情况而定(一旦block被赋值就会被copy到堆上),将栈上的block,copy到堆上,这是因为栈上的内存是有系统控制的,栈block出了作用域就会被销毁,为了延长block的生命周期,将其copy到堆上
全局block:生命周期从创建开始到程序结束,相当于单例的存在,它不会用到临时变量,只会用到全局或静态变量或者没有用到变量
block对外界变量的捕获
block可以捕获任何外界变量,捕获到之后会将其作为自身结构体的成员变量,其中局部变量,静态变量会被追加到block结构体的构造函数中,从clang翻译的源码中可以看到:
void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val));
block的构造函数追加了局部变量的值,和静态变量的地址
static int j = 10;
inti =10;
void(^block)(void) = ^{
NSLog(@"打印i:%dj:%d",i,j);
};
i++;
j++;
block();
打印结果为i:10,而不是11,因为block只捕获了局部变量的值,没有捕获地址,block内部会用到什么,在它创建的时候就会去捕获什么,这时候捕获到的也只能是值,为了防止局部变量之后被销毁,需要知道它的值的时候取不到了,所以是提前捕获,后续改变量的值在怎么变化,block取的都是最初捕获的值. j:11,因为j是静态变量捕获了它的地址,所以可以实时获取j的值。
block内部修改外部变量
如果block内部直接修改外部变量,编译器会提示我们要加上__block ,为什么需要加上__block就可以修改了呢?
1.block捕获外界局部变量只是捕获了它的值,并没有捕获它的地址,对它只有只读能力,没有写的能力
2.被__block修饰之后,该变量会被转换为一个结构体:
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
结构体中,它里面包含了isa,forwarding指针,flag标示,size大小,变量值,名字和变量名同名,在block的构造函数中 我们可以看到,追加了forwarding指针,block可以通过forwarding指针访问变量,进而操作变量。
网友评论