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的问题。

posted @ 2015-08-12 12:44  前尘如梦  阅读(109)  评论(0)    收藏  举报