一、存储变量本质分析
先看下面这段代码和运行结果。
int number=100;
int (^testBlock)(int)=^(int x){
return number+x;
};
NSLog(@"调用block的结果为:%d",testBlock(100));
number=200;
NSLog(@"改变number的值后,再次调用block的结果为:%d",testBlock(100));
分析:第一次调用block块的时候 x为100 number也是100 应该输出200 看运行结果:
2016-05-25 09:42:29.805 NewStyle[738:19498] 调用block的结果为:200
那么接下来,我们改变了number的值,再次调用block的时候 x仍为100,number为200,那么结果会不会是300呢?看运行结果如下:2016-05-25 09:42:29.806 NewStyle[738:19498] 改变number的值后,再次调用block的结果为:200
结论: 为什么结果是200 ?而不是300 呢?那会不会是block记录了第一次number的值,后面修改不起作用呢???如果是个指针会是什么样子的呢?看下面代码:
int a=2;
int *p=&a;//p指针指向值为2的变量a的地址
int (^testBlock2)(int)=^(int y){
return *p+y;
};
NSLog(@"调用block的结果为:%d",testBlock2(2));//4
NSLog(@"p指针取值:%d 变量a的值:%d ",*p,a);//2 2
*p=4;
NSLog(@"改变指针p对应的值以后再次调用block的结果为:%d",testBlock2(2));//6
NSLog(@"修改指针p对应的值以后,p指针取值:%d 变量a的值:%d",*p,a);//4 4
我们定义一个普通局部变量a并初始化为2,接下来定义一个指针p指向a。毫无疑问,p指针指向地址的值为2。那么此时运行如下
2016-05-25 09:58:15.757 NewStyle[769:26498] 调用block的结果为:4
2016-05-25 09:58:15.757 NewStyle[769:26498] p指针取值:2 变量a的值:2
当我们通过p指针修改其所指地址的值时,这里修改p指针所指内容为4。运行效果如下:2016-05-25 09:58:15.757 NewStyle[769:26498] 修改指针p对应的值以后,p指针取值:4 变量a的值:4
结论: testBlock 在其主体中用到的 number 这个变量值的时候做了一个 copy 的动作,把 number 的值 copy 下来。所以,之后 number 即使换成了新的值,对于 testBlock 里面 copy 的值是没有影响的。需要注意的是,这里copy的值是变量的值,如果它是一个记忆体的位置(地址),换句话说,如果这个变量是个指针的话,那么此时对testBlock里面copy的值是有影响的。那么在block里面可不可以通过修改指针的值呢?看看如下代码: int b=10;
int *pointer=&b;
int (^testBlock3)(int)=^(int z){
*pointer=20;//我们前面提到普通局部变量(非指针)在block里面如果不加__block修饰 是不可以修改的 会报编译错误。这里貌似没有报编译错误,
return *pointer+z;
};
NSLog(@"看看结果 %d",testBlock3(10));//30?
这里定义了一个指针, 并指向了局部变量b。那么此时指针pointer所指的地址的值就是变量b的值,毫无疑问就是10。接下来在block块里面我们通过指针修改了pointer指针的值。运行结果如下:
2016-05-25 09:58:15.756 NewStyle[769:26498] 看看结果 30
结论,指针型变量在block里面可以修改。
二、Block高级应用举例——两个控制器之间的传值
那么,我这里有两个控制器 ViewController和myTestVC 通过在ViewController里面点击一个按钮触发事件推出控制器myTestVC。
ViewController里面的代码如下:
//按钮触发事件 推出控制器myTestVC
-(void)btnClicked:(UIButton *)Sender{
myTestVC *vc =[[myTestVC alloc]init];
//这里会回调
vc.responseStr = ^(NSString *str){
UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:@"Block高级应用" message:str delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil,nil];
[alertView show];
};
[self.navigationController pushViewController:vc animated:YES];
}
在myTestVC.h中申明如下block。
/**
* 定义了一个response的Block。这个response必须带一个参数,这个参数的类型必须为id类型的
* 无返回值
* @param id
*/
typedef void(^response)(id);
@interface myTestVC : RootViewController
/**
* 用上面定义的response声明一个Block,声明的这个Block必须遵守声明的要求。
*/
@property (nonatomic, copy) response responseStr;
@end
在myTestVC.m中,在即将返回上一层视图控制器(这里指返回ViewController喽)的事件中,添加如下代码:
//返回上一层控制器事件
-(void)leftTextItemClicked:(UIBarButtonItem*)item{
//you should override this method when you use it in a subclass of RootViewController.
//声明一个字符串
NSString *responseString = @"我们回来啦!我们回来啦!";
//用刚刚声明的那个Block去应答 并传递到上一个VC 这里肯定是ViewController啦
self.responseStr(responseString);
[self.navigationController popViewControllerAnimated:YES];//返回到上一层视图控制器
}
运行下,看看效果如何。
至此,Block的基本用法差不多就这些喽,后面继续分享其他有趣的知识。