Blocks
Blocks
Blocks语法
基本的表达式语法如下:
^ 返回值类型 参数类型 表达式
举例:^ int (int count){return count + 1 ;}
block语法可以省略返回值类型,此时如果没有return语句,就返回void类型,如果含有多个return,则各个return的返回类型必须一致,表达式语法如下:
^ 参数类型 表达式
举例:^ (int count){return count + 1 ;}
block如果没有参数,则可以忽略参数列表。表达式语法如下:
^ 表达式
举例:^ {NSLog(@"hello world ,blocks") ;}
Block变量
基本的表达式语法如下:
返回值类型 (^ 变量名称) (参数类型);
举例:int (^ count)(int);
变量名称实际是一个指针,指向匿名函数,但是使用前必须定义,使用的时候也要写完格式,为了简化记述方式,可以用typedef来定义,从而可以像函数指针类型一样使用。
typedef 返回值类型 (^ 变量名称) (参数类型);
举例:typedef int (^ counts)(int);
调用的时候:
void func(int (^counts)(int))
就可以简写为:
void func(counts count)
函数内使用直接使用count(10)即可。
截获自动变量
Block在赋值的时候,会自动截取变量的值,即保留该自动变量的瞬间值。在block赋值语句后,再次改变自动变量的值,也不会使得block调用的值发生改变。
举个例子:
int main(){
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{printf(fmt,val);};
val = 2;
fmt = "These values were changed ,but not show. val = %d\n";
blk();
return 0;
}
最后执行输出为:val = 10。可见即使blk();调用在变量改变值之后,最后输出的值还是在定义的时候的值。
__block说明符
如果要在Block语法的表达式中将值付给Block语法外声明的自动变量,则需要在该自动变量上附加__block说明符。
int val = 0;
void (^blk)(void) = ^{val = 1;};
blk();
printf("val = %d\n", val);
比如上面的例子中,尝试在block赋值表达式中给外面的变量赋值,就会导致编译错误。
在外面的变量中加上__block,就能在block中进行赋值。
如果截获的是Object-C对象,调用对象的方法不会产生编译错误,因为截获的是对象的实例指针。但是重新赋值对象就会产生编译错误。
另外,使用C语言的数组时必须小心使用其指针。比如
const char text[] = "hello";在block中调用text[2]时就会出现错误,应使用const char *text[] = "hello";指针定义才可以调用。因为Blocks截获的自动变量方法并没有实现对C语言数组的截获,这个时候是同指针就可以解决问题。
Block循环引用
如果Block中使用附有__strong修饰符的对象类型自动变量,那么当Block从栈复制到堆的时候,该对象为Block所持有。这样容易引起循环引用问题。
比较典型的例子就是在Block表达式中调用self,由于Block定义在对象中,对象的self即持有对象,而Block也调用self,都是强引用,就会导致循环引用,无法释放。此时应该使用id __weak tmp = self;语句,将self赋值给一个弱引用,然后再在Block中调用弱引用。
还有在Block中没有使用self,但是调用了self中定义的强引用对象属性,也会导致隐式调用self,因为对象的强引用是对象结构体的成员变量,实际调用就是self->obj。此时也应该使用id __weak obj_ = obj;来避免循环引用。
使用__block修饰符也可以用来避免循环引用,通过
block变量可以控制对象的持有期间,但是必须执行Block,而且需要在Block中将block变量释放,即赋值为nil。
typedef void(^blk_t)(void);
blk_t blk_;
__block id tmp = self;
blk_ = ^{
NSLog(@"slef = %@",tmp);
tmp = nil;
};
blk_();
比如上例中,不会产生循环引用。但是如果不调用blk_()来执行block,就会导致循环引用。因为block变量tmp持有了self和Block,self持有Block,就会导致循环引用。但是执行了blk_(),就会将tmp赋值为nil。就取消了tmp持有self的问题。

浙公网安备 33010602011771号