1.简介
- block是代码块,其本质和变量类似。不同的是代码块存储的数据是一个函数体。使用Block,就可以像其他标准函数一样,传入参数,并得到返回值。
- block是一种比较特殊的数据类型,我们可以把Block当作是Object-C的一种匿名函数。常见的场景有:界面传值,带代码进行封装作为参数传递等。
- block分为三种类型
1、 NSConcreteStackBlock ,存储在栈上,在block内部引用外部变量,在函数退出的时候,block所占的内存空间会被回收;
2、NSConcreteGlobalBlock,类似函数,存储在程序的数据区域(text段),在block内部没有引用任何外部变量;
3、NSConcreteMallocBlock,存储在堆上,一般是对NSConcreteStackBlock进行copy操作获取。
2.定义和使用
a.定义
返回值(^ block变量)(形参列表) = ^(实参列表) {
行为主体
};
b.使用
b-1.无返回值无参数
void(^block)() = ^(){
NSLog(@"这是一个无返回值无参数的block");
};
block();
b-2.有返回值无参数
NSString *(^block)() = ^() {
return @"这是一个有返回值无参数的block";
};
NSLog(@"%@",block());
b-3.无返回值有参数
void(^block)(int) = ^(int x) {
NSLog(@"这是一个无返回值有参数的block,x=%d",x);
};
block(10);
b-4.有返回值有参数
int (^block)(int) = ^(int x) {
NSLog(@"这是一个有返回值有参数的block");
return x*2;
};
int y = block(10);
NSLog(@"x*2===%d",y);
3.__block关键字的使用
在block的代码块(行为主体)中,我们是不能直接修改外面定义好的变量的。如下代码所示:
int a = 5;
int (^block)(int) = ^(int x) {
x = x * a;
return x;
};
a += 5;
NSLog(@"===%d,===%d",a,block(10));
以上代码输入结果:===10,===50
原因
block分配的位置默认是栈上分配,在执行的时候会把变量内容拷贝到自己的栈内存上,以便执行的时候可以调用,就以上例子来看,当block去拷贝变量的时候发现 a是一个常量,所以回去引用a的指针,但是它对变量的内容并不是做深拷贝,所以不会去重新申请内存。解决方案
在block使用中,但我们需要改变block内部能够读取外部局部变量的值时,我们需要给这个值附加上__block修饰符。将以上代码修改成:
__block int a =5;
int (^block)(int) = ^(int x) {
x = x * a;
return x;
};
a += 5;
NSLog(@"===%d,===%d",a,block(10));
以上代码输入结果:===10,===100
4.block循环引用
循环引用指的是两个对象互相强引用了对方,从而导致谁也释放不了谁的内存泄漏。
会出现的原因:某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身。
解决方案:
__weak typeof (self) weakSelf = self;
__weak someClass *weakSelf = self;
5.block中界面间传值的使用
- 思路:
1.创建两个视图FirstViewController和SecondViewController。
在FirstViewController视图中添加一个UIButton和一个UILable,UIButton的点击事件为跳转(push)到视图SecondViewController。
在SecondViewController视图中也添加一个UIButton和一个UITextField,UIButton的点击事件为跳转(pop)回视图FirstViewController。
2.实现的效果:当从SecondViewController视图跳回FirstViewController视图时,让FirstViewController中的UILabel显示SecondViewController的UITextField中的文字。 - 实现代码
//SecondViewController.h
#import <UIKit/UIKit.h>
typedef void(^textBlock)(NSString *text); //方式一
@interface SecondViewController : UIViewController
@property (nonatomic, copy) textBlock block;//方式一
@property (nonatomic,copy) void(^colorBlock)(UIColor *color);//方式二
@end
//SecondViewController.m
#import "SecondViewController.h"
@interface SecondViewController ()
@property (weak, nonatomic) IBOutlet UITextField *textFiled;//xib实现
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 由于是用xib做的视图,这里什么也不需要实现,主要看点击事件
}
- (IBAction)btnClick:(id)sender {
self.block(self.textFiled.text);//看这里,将textFiled上文字传递回去
self.colorBlock(self.view.backgroundColor);//看这里,将背景颜色传递回去
[self.navigationController popViewControllerAnimated:YES];
}
@end
FirstViewController.h中没有实现什么,所以此处就不贴FirstViewController.h的代码了。
//FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
@interface FirstViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 由于是用xib做的视图,这里什么也不需要实现,主要看点击事件
}
- (IBAction)btnClick:(id)sender {
SecondViewController *vc = [[SecondViewController alloc] init];
vc.block = ^(NSString *text) {
self.label.text = text;
};
vc.colorBlock = ^(UIColor *color){
self.label.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];
}
@end