block的概念及基本使用
block 是实质上是代码段,但它不像方法有方法名,所以就定义一种block类型来接收
block
* block是一种数据类型, 可以使用这种数据类型定义变量, 并赋值。
* block数据类型在使用前需要先定义该数据类型, 然后再使用(就像使用Person类一样, 先 定义一个Person类, 然后再通过Person类声明一个Person类型变量)。
* block这种数据类型用来保存一个方法、函数、一段代码
* 必须掌握: block的定义语法、使用场景
* 使用inlineBlock辅助编写block代码
//int (^block)(int,int) = ^(int n, int m){}; int (^block)(int,int) = ^(int n, int m){};
block的typedef 形式和函数指针的用法很像,先来回顾下
1、函数指针回顾
int (*p)(int x,int y); //其中p是函数指针变量名,这个p指针的含义是它只能指向返回值是int型,有二个参数,而且参数是int型的函数 用法:p = sum; p = jian; //方法名本身存放的就是地址,所以不用取地址,所以p可以指向上面 jia jian chen chu 四个函数,是不是很省事 调用:p(2,3); //其实你写成(*p)(2,3)也不会报错。*p就是这个地址
2)函数指针别名
typedef int (*NewType)(int x,int y); NewType f1,f2,f3; //f1实际类型 int (*f1)(int x,int y);
加了typedef,NewType这就不是一个函数指针,而是一个新的类型,它定义了f1,f2,f3三个NewType类型的数据,他们指向的是它只能指向返回值是int型,有二个参数,而且参数是int型的函数
实际的用途就是简化函数定义的书写
2、block的typedef
利用typedef定义block类型(和指向函数的指针很像)
格式: typedef 返回值类型 (^新别名)(参数类型列表);
Typedef int(^MyBlock)(int ,int);
block访问外部变量
这里说明了二个问题很重要的问题
1: block内部可以访问外部值的问题,但是注意,这是一个新的内存空间变量
2: 在block内部不可以修改block外部的变量
#import <Foundation/Foundation.h> /** * block内部访问外部值的问题 * block内部不允许修改外部变量值 */ int main(int argc, const char * argv[]) { @autoreleasepool { int m = 10; NSLog(@"1:m = %d",m); //10 NSLog(@"2:m addr = %p",&m); //栈区 //定义变量,并且赋值 //当定义block的时候,block会把外部变量以const的方式(常量)复制一份 //存放到block的所在的内存中 void (^myBlock)()=^{ //m的值不能被修改 //m = 100; NSLog(@"5:m addr = %p",&m); //堆区 //可以访问m的值 NSLog(@"3:in block m = %d",m); };
NSLog(@"4:m addr = %p",&m); //栈区 //使用 myBlock(); } return 0; }
2015-07-29 16:46:54.865 3-【掌握】block访问外部变量[868:146191] 1:m = 10 2015-07-29 16:46:54.866 3-【掌握】block访问外部变量[868:146191] 2:m addr = 0x7fff5fbff6ec 2015-07-29 16:46:54.866 3-【掌握】block访问外部变量[868:146191] 4:m addr = 0x7fff5fbff6ec 2015-07-29 16:46:54.866 3-【掌握】block访问外部变量[868:146191] 5:m addr = 0x100300120 2015-07-29 16:46:54.866 3-【掌握】block访问外部变量[868:146191] 3:in block m = 10
3: 默认情况下,Block内部不能修改外部的局部变量 ,但可以在块内重新定义m,此时定义的m在栈区,可以说局部变量屏蔽了相对的全局变量
4:给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改,不再以const的方式拷贝
5:用了__block后,以后使用的都是block里面堆区的变量 m
#import <Foundation/Foundation.h>
n=0; int main(int argc, const char * argv[]) { @autoreleasepool { __block int m = 10; NSLog(@"1:m add = %p",&m); //栈区地址 //__block 不在以const的方式拷贝 void (^myBlock)()=^{ //m的值不能被修改 m = 100; //堆区的m被修改成100 // n = 10; //全局变量可以直接修改 // int m = 100; //默认情况下,Block内部不能修改外部的局部变量 ,但可以在块内重新定义m,此时定义的m在栈区
NSLog(@"5:m addr = %p",&m); //堆区 //可以访问m的值 NSLog(@"3:in block m = %d",m); // ? };
myBlock(); NSLog(@"6:m = %d",m); // 100 //用了__block后,以后使用的都是block里面堆区的变量 m NSLog(@"7:m addr = %p",&m); } return 0; }
2015-07-29 16:59:28.018 3-【掌握】block访问外部变量[878:150305] 1:m add = 0x7fff5fbff708 2015-07-29 16:59:28.019 3-【掌握】block访问外部变量[878:150305] 5:m addr = 0x1001205d8 2015-07-29 16:59:28.019 3-【掌握】block访问外部变量[878:150305] 3:in block m = 100 2015-07-29 16:59:28.019 3-【掌握】block访问外部变量[878:150305] 6:m = 100 2015-07-29 16:59:28.020 3-【掌握】block访问外部变量[878:150305] 7:m addr = 0x1001205d8
4-【理解】应用:block的使用场景1
#import <Foundation/Foundation.h> //void (^workBlock)() // block类型的变量 workBlock // 作为函数的参数 void work(void (^workBlock)()){ //思路代码,你发现每天不一样的就一处 ,那只有block才能传代码块 NSLog(@"起床"); NSLog(@"刷牙"); NSLog(@"去车站"); NSLog(@"坐车"); // NSLog(@"了解项目"); //思考: 把这句话作为参数传递过来 workBlock(); NSLog(@"去车站"); NSLog(@"坐车回家"); NSLog(@"吃饭"); NSLog(@"睡觉"); } //每天做什么事情 void workDay(int n){ // 定义别名 typedef void (^workBlock) (); workBlock w; //w是变量 switch (n) { case 1: w =^{ NSLog(@"----->了解项目"); }; break; case 2: w = ^{ NSLog(@"----->分析项目"); }; break; case 3: w = ^{ NSLog(@"----->写项目"); }; break; case 4: w = ^{ NSLog(@"----->调试项目"); }; break; case 5: w = ^{ NSLog(@"----->离职"); }; break; default: break; } //调用work函数 work(w); } //void day1(){ // // NSLog(@"起床"); // NSLog(@"刷牙"); // NSLog(@"去车站"); // NSLog(@"坐车"); // // NSLog(@"了解项目"); // // NSLog(@"去车站"); // NSLog(@"坐车回家"); // NSLog(@"吃饭"); // NSLog(@"睡觉"); //} // //void day2(){ // // NSLog(@"起床"); // NSLog(@"刷牙"); // NSLog(@"去车站"); // NSLog(@"坐车"); // // NSLog(@"分析项目"); // // NSLog(@"去车站"); // NSLog(@"坐车回家"); // NSLog(@"吃饭"); // NSLog(@"睡觉"); //} // // //void day3(){ // // NSLog(@"起床"); // NSLog(@"刷牙"); // NSLog(@"去车站"); // NSLog(@"坐车"); // // NSLog(@"写代码"); // // NSLog(@"去车站"); // NSLog(@"坐车回家"); // NSLog(@"吃饭"); // NSLog(@"睡觉"); //} // // //void day4(){ // // NSLog(@"起床"); // NSLog(@"刷牙"); // NSLog(@"去车站"); // NSLog(@"坐车"); // // NSLog(@"调试程序"); // // NSLog(@"去车站"); // NSLog(@"坐车回家"); // NSLog(@"吃饭"); // NSLog(@"睡觉"); //} // //void day5(){ // // NSLog(@"起床"); // NSLog(@"刷牙"); // NSLog(@"去车站"); // NSLog(@"坐车"); // // NSLog(@"离职"); // // NSLog(@"去车站"); // NSLog(@"坐车回家"); // NSLog(@"吃饭"); // NSLog(@"睡觉"); //} int main(int argc, const char * argv[]) { @autoreleasepool { // day1(); // day2(); // day3(); // day4(); // day5(); for (int i=1; i<=5; i++) { workDay(i); } } return 0; }
block作为函数的返回值
返回的是一个类型,那你必须是是哥类型,所以必须用到typedef,定义一个新类型
#import <Foundation/Foundation.h> //返回值是block类型的 //void (^block)(); //定义新的block类型 //1) 使用 typedef 定义一个新的类型 typedef void (^newType)(); //block类型作为函数的返回值 //2) 用新定义的类型作为函数的返回值 newType test(){ //定义block变量 newType w1 = ^{ //定义一个newType类型的变量w1用来接收block代码块 NSLog(@"xxxxx"); NSLog(@"hello world"); }; return w1; //返回值block } //重新定义了一个新的类型 newType2 typedef int(^newType2)(int ,int ); newType2 test2(){ //返回一个特殊的"值" 这个值是一个有返回值,有两个参数的的代码块 return ^(int a,int b){ //相当于把一个代码块返回给了n2 return a+b; }; } int main(int argc, const char * argv[]) { @autoreleasepool { //定义block类型的变量 //3) 定义block变量接收 函数返回的结果 newType n1 = test(); // 定义一个newType类型的变量n1用来接收返回值w1,我
//4) 执行block
n1(); //这个例子完全看不出这个定义函数的优势和作用 /* n2 =^(int a,int b){ return a+b; }; */ newType2 n2 = test2(); //调用block int s = n2(23,12);
NSLog(@"s = %d",s); } return 0; }
【总结】代码块定义的本身就比较怪异,导致它的扩展方法也不是容易记,可能这需要一个熟悉的过程
应用:block的使用场景2
思考&实现1:
用返回值是block的函数,修改我们应用场景的代码
#import <Foundation/Foundation.h> //void (^workBlock)() // block类型的变量 workBlock // 作为函数的参数 // 给无参无返回值的block起个别名(新的类型) typedef void(^blockType)(); void work(int n){ NSLog(@"起床"); NSLog(@"刷牙"); NSLog(@"去车站"); NSLog(@"坐车"); // NSLog(@"了解项目"); //思考: 把这句话作为参数传递过来 //workBlock(); //声明 workDay这个函数 blockType workDay(int n); blockType w = workDay(n); w(); NSLog(@"去车站"); NSLog(@"坐车回家"); NSLog(@"吃饭"); NSLog(@"睡觉"); } //每天做什么事情 blockType workDay(int n){ blockType w; //w是block变量 switch (n) { case 1: w =^{ NSLog(@"----->了解项目"); }; break; case 2: w = ^{ NSLog(@"----->分析项目"); }; break; case 3: w = ^{ NSLog(@"----->写项目"); }; break; case 4: w = ^{ NSLog(@"----->调试项目"); }; break; case 5: w = ^{ NSLog(@"----->离职"); }; break; default: break; } //返回值是block类型的 return w; } int main(int argc, const char * argv[]) { @autoreleasepool { for (int i=1; i<=5; i++) { work(i); } } return 0; }