八、Object-C 块对象
块对象定义
语法:^ (参数列) { 主体 }
定义块对象,以下代码输出5,12。
void (^a)(int) = ^(int i){ printf("%d\n",i); }
a(5);
a(6 * 2);
块对象使用
typedef int (^block)(int);
block a = ^(int i) { return i+1; }
int (^blocksa[3])(int);
block blocksb[3];
void (^blocka)(void) = ^(void){ /*....*/ };
void (^blockb)(void) = ^{ /*....*/ };
void (^blockc) () = ^() { /*....*/ };
1、typedef int (^block)(int);定义一个返回值为int型的块对象,并用typedef简化声明
2、block a = ^(int i) { return i+1; }利用声明定义一个a的块对象,并实现快对象。
3、bloksa和blocksb类似,代表定义了含三个block的块对象的块数组。
4、blocka,blockb,blockc是三种定义方式,代表参数列为无参时可以省略,或只保留括号
块对象的捕获
我们先看一个很有意思的例子
int a = 1;//全局变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
static int b = 2;//静态变量
int c = 3;//局部变量
void (^block)(void) = ^ {
printf("a = %d, b = %d, c = %d \n",a,b,c);
};
block();
a = 4;
b = 5;
c = 6;
block();
block = ^ {
printf("a = %d, b = %d, c = %d \n",a,b,c);
};
block();
}
return 0;
}
打印结果如下:
2018-08-29 15:06:59.492420+0800 test[15881:1509181] Hello, World!
a = 1, b = 2, c = 3
a = 4, b = 5, c = 3
a = 4, b = 5, c = 6
Program ended with exit code: 0
由此输出我们可以对块主体访问变量总结如下:
1、全局变量和静态变量,可以访问和改变他们的值。
2、局部变量(栈内变量)只能访问,不能修改。因为它是先被保存起来,然后再访问,所以原本的变量哪怕变了,块主体内也不知道。如果在块内容中修改其值,会抱错。带入块的参数也属于局部变量。
3、额外一点,块内不能访问数组,否则会报错:Cannot refer to declaration with an array type inside block
由此,捕获的概念就出来了
捕获:块主体会把栈内变量等执行环境封装起来,块内读取块之外的变量。
__block变量
理解__block变量我们也用一个例子来说明
int a = 0;
void (^block)(void) = NULL;
void func(int n){
__block int b = 0;
void (^b1)(void) = ^{
b += 1;
printf("n = %d, a = %d, b = %d\n",n,++a,b);
};
void (^b2)(void) = ^{
b += 100;
printf("n = %d, a = %d, b = %d\n",n,++a,b);
};
b1();
b2();
block = [b1 copy];
b = 10000;
n = 99999;
b2();
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
void (^b)(void) = NULL;
func(1);
b = block;
b();
func(2);
b();
}
return 0;
}
输出如下:
2018-08-30 10:35:22.751335+0800 test[24294:2032994] Hello, World!
n = 1, a = 1, b = 1
n = 1, a = 2, b = 101
n = 1, a = 3, b = 10100
n = 1, a = 4, b = 10101
n = 2, a = 5, b = 1
n = 2, a = 6, b = 101
n = 2, a = 7, b = 10100
n = 1, a = 8, b = 10102
Program ended with exit code: 0
1、同一个__blick变量作用域内,块共享__block变量。
2、__block变量是在同一作用域的块对象执行时动态生成,若有有一个块对象还存在,__block变量就存在。由此,被复制的块对象也是能共享__block变量。
3、注意看最后一条输出,道理就是第二条说的,因为copy的block对象作用域还是和copy之前的b1作用域相同。所以__block变量还是之前的值。当然n是局部变量,也是同样道理。
4、ARC下只能用copy方法,原本还有一个叫Block_copy的方法,这里不能用因为已经定义(void *)形参指针。