- 会如何定义一个block
- blcok变量的初始化
- 简化block的定义
- 会在block内部访问与修改外部变量
- 会将block作为参数传递
- 知道block在哪
1.如何定义一个block?
1). block是1个数据类型,我们可以声明block类型的变量. block类型的变量中专门来存储1段代码的.
这段代码可以返回值可以有参数.
2). block类型的变量,虽然是用来存储1段代码的.
但是,1个block变量中并不是任意的1段代码都可以存储的.
而是有限定的.
在声明1个block类型的变量的时候 必须要指定这个block变量中可以存储的代码的参数和返回值描述.
一旦指定,这个block变量中只能存储这样的代码段.别的格式的代码段是无法存储进去的.
3). 如何去声明block类型的变量.
a. 根据刚刚的解释,声明block变量的时候,要指定这个这个block变量中存储的代码段的返回值和参数.
b. 声明block变量的语法:
返回值类型 (^block变量名称)(参数列表);
void (^myBlock)();
代表声明了1个block类型的变量,变量名加做myBlock.
这个变量中只能存储没有返回值,也没有参数的1段代码.
int (^myBlock)();
代表声明了1个block类型的变量,变量名加做myBlock.
这个变量中只能存储返回值为int类型的,没有参数的1段代码.
int (^myBlock)(int num1,int num2);
代表声明了1个block类型的变量,变量名加做myBlock.
这个变量中只能存储返回值为int类型的,并且有两个整型的参数的1段代码.
c. 特别注意:
1个block变量中只能存储和这个block变量要求相同的1短代码.
2.block变量的初始化
1). 初始化block变量的原理: 写1段符合block变量要求的代码,把这段代码存储到这个block变量中.
2). 书写1个block代码段的语法格式.
^返回值类型(参数列表){
代码;
};
3). 无参数无返回值的代码段.
^void(){
NSLog(@"我爱你");
NSLog(@"打瞌睡的小女孩");
};
这个时候,就可以将这段代码 通过赋值符号. 赋值给无参数,无返回值的block变量.
void (^myBlock1)();
myBlock1 = ^void(){
NSLog(@"我爱你");
NSLog(@"打瞌睡的小女孩");
};
代表的意义:myBlock1变量中存储了这段代码;
4). 有返回值无参数的代码段.
注意: 如果代码段标识了有返回值,那么在代码段结束之前,必须要使用return返回1个数据.
^int(){
int num1 = 10;
int num2 = 20;
int num3 = num1 + num2;
return num3;
};
5). 有返回值有参数的代码段.
^int(int num1,int num2){
int num3 = num1 + num2;
return num3;
};
6). 特别强调: block变量中只能存储和这个block变量要求相同的代码段.否则就会报语法错误.
3.简化block的定义
1). 感觉到隐隐不爽的地方: 声明1个block变量代码太长了.
2). 将长的block变量定义变得短一些.
3). typedef: 为1个已经存在的数据类型取1个别名.
当1个数据类型太长的时候,我们就可以使用typedef给它定义1个短别名,这样方便.
4). typedef也可以将1个长的block类型定义为1个短类型.
语法格式: typedef 返回值类型 (^新类型名称)(参数列表);
typedef void (^NewType)();
代表新定义了1个类型 这个类型的名字叫做NewType.
这是1个无参数无返回值的block类型.
如果我们要定义1个无参数无返回值的block变量.直接使用NewType就可以定义了.
typedef int (^NewType)(int num1,int num2);
代表新定义了1个数据类型. 这个类型的名字叫做NewType.
这是1个返回值为int 有两个整型参数的block类型.
如果我们要定义1个返回值为int 有两个整型参数的block类型变量. 直接使用这个新类型就可以定义.
4. 会在block内部访问与修改外部变量
1). 在block代码块的内部可以定义1个和外部的变量名称相同的变量.
访问的时候,是就近原则.
2). 在block代码块的内部可以去取出外部的全局变量和局部变量的值.
但是,在block代码块的内部,可以修改全局变量的值,但是不能修改外部的局部变量的值.
3). 如果就是希望在block代码块的内部去修改外部的局部变量的值.
那么就给这个局部变量加个__block的修饰符.
4). block修改外部变量
// 类型 变量名 = 值;
__block int age = 100;
NSLog(@"====%p",&age); // 栈
// 使用__block修饰外部变量时, 在定义完成block之后,栈中的外部变量就是一个摆设,没有什么卵用了
void (^block)() = ^{
age = 200;
NSLog(@"block中的age = %d",age); // 200
NSLog(@"我是block = %p",&age); // 堆
};
NSLog(@"====%p",&age); // 栈
NSLog(@"age = %d",age); // 100
// 执行block
block();
NSLog(@"====%p",&age); // 堆
NSLog(@"age = %d",age); // 200
5.将block作为参数传递
1). block是1个数据类型.
那么block就必须可以作为方法/函数的参数.
2). 如何为函数写1个block类型的参数.
1> 只需要在函数的小括弧中写声明1个block类型的变量就可以.
void test(void (^block1)());
代表,这个函数有1个block参数.无返回值无参数的block参数.
2> 如果你觉得晕.
那么就可以使用typedef将block类型定义的短1些.
typedef void (^NewType)();
void test(NewType b1)
3). 如何调用带block参数的函数.
1> 如果调用的函数的参数是1个block类型的.
那么要求传递1个和形参block类型相同的block变量
可以先将符合要求的代码存储在1个block变量中,然后传递这个变量.
2> 可以直接传递符合要求的代码段.
test( ^{
NSLog(@"啦啦啦啦啦,我是打瞌睡的小行家");
NSLog(@"不等天明打瞌睡");
NSLog(@"太阳出来还在打!");
});
4. 把block作为函数的参数能够实现什么效果?
之前的函数的参数能做什么事情: 可以将调用者的数据传递到函数的内部去使用.
block作为参数: 可以将调用者的1段代码传递到函数的内部去执行.
5. 什么时候需要将block作为参数?
1个函数代表1个相对对独立的功能.
函数在完成这个功能的时候,发现需要执行1段代码才可以继续完成.
而这段代码函数内部不确定是什么样的代码
只有调用者确定.
那么这个时候,就可以将block作为函数的参数.
让调用者在调用这个函数的时候 把这段代码传递进来.
放在这里执行.
6.知道block在哪
block有三种类型:
1> 全局block:
- 不管是ARC还是MRC,block内部没有访问任何外部变量或访问的外部变量是静态变量时,block就是全局block
2> 堆block:
- 在MRC下,给block调用copy方法,会将栈block拷贝到堆中.
- 在MRC环境下,如果block是堆区block,需要程序猿手动release
3> 栈block:
- 在MRC环境下,block内部访问了外部变量时且不是静态变量,block就是栈block
最后分享一个关于block是如何实现的技术博客:谈Objective-C block的实现-唐巧