本文将介绍一下iOS开发中的用到的新语法:block
首先来看看block变量的定义和使用。
- (void)testBlock {
/********************无参数、无返回值的block********************/
// 1.定义一个无参数无返回值的block变量
void (^block1)() = ^() {
NSLog(@"this is a block");
};
// 2.使用该block
block1();// 打印:this is ablock
/********************有参数、有返回值的block********************/
// 1.定义一个无参数无返回值的block变量
int (^block2)(int, NSString *) = ^(int age, NSString *name) {\
NSLog(@"name = %@", name);
return 20;
};
// 2.使用该block
int number = block2(20, @"jack");// 打印:name = jack
NSLog(@"%d", number);// 打印:20
/********************方法外的block********************/
block();// 打印:out function
}
void (^block)() = ^(){
NSLog(@"out function");
};
其他情况的block就不一一举例了。这上面的例子可以看出定义block变量,就相当于定义了一个函数。但是区别也很明显,因为函数肯定是在方法外面定义,而block变量定义在了方法内部。当然,我们也可以把block定义在方法外部,例如上面的代码块block的定义,就在-(void)testBlock外面。
可以看出在定义block的地方,并不会执行block{}内部的代码,而在block调用之后才会执行其中的代码,这跟函数的理解其实差不多。即只有在调用block(函数)的时候才会执行block体内(函数体内)的代码。
block作用:存储代码,等到调用它的时候再执行
__block关键字的使用
在block的{}体内,是不可以对外面的变量进行更改的。
编译直接报错。如果我们就是需要在block中修改变量的值,这个时候怎么办呢?我们可以使用__block关键字来定义该变量。利用__block定义的变量是可以在block中修改该变量的值的
- (void)testBlock {
__block int number = 10;// 使用__block定义变量
// 定义一个block修改变量number的值:number + 1
int (^myBlock)(int) = ^(int a) {
number = number + a;
return number;
};
// 调用block
int result = myBlock(10);
NSLog(@"result = %d", result);// 打印:20
}
block类型
现在我们学会如何定义、使用block了。那么看看下面这个例子
- (void)testBlock {
void (^block1)() = ^() {
int num = 10 + 20;
NSLog(@"block1 - %d", num);
};
void (^block2)() = ^() {
int num = 10 - 20;
NSLog(@"block2 - %d", num);
};
void (^block3)() = ^() {
int num = 10 * 20;
NSLog(@"block3 - %d", num);
};
void (^block4)() = ^() {
int num = 10 / 20;
NSLog(@"block4 - %d", num);
};
block1();// 打印:block1 – 30
block2();// 打印:block2 – -10
block3();// 打印:block3 – 200
block4();// 打印:block4 – 0
}
以上4个block都是没有返回值,也没有参数,即4个block属于同一个类型。如果每次定义这样的block有没有觉得好麻烦。其实在OC中我们可以使用关键字来定义这种类型。
我们先从简单的定义类型:
typedef int MyInt;
- (void)test {
MyInt a = 10;
NSLog(@"a = %d", a);
}
上面我们使用了typedef给int类型起了一个别名MyInt,这样int能使用的地方,MyInt也能使用。其实block类型也是一样的。如下:(和定义整型对比看)所以上面加减乘除的例子可以如下:
typedef void (^BlockType)();
- (void)testBlock {
BlockType blockSum = ^() {
int num = 10 + 20;
NSLog(@"block4 - %d", num);
};
BlockType blockSub = ^() {
int num = 10 - 20;
NSLog(@"block4 - %d", num);
};
BlockType blockMul = ^() {
int num = 10 * 20;
NSLog(@"block4 - %d", num);
};
BlockType blockDivide = ^() {
int num = 10 / 20;
NSLog(@"block4 - %d", num);
};
blockSum();// 打印:block1 – 30
blockSub();// 打印:block2 - -10
blockMul();// 打印:block3 – 200
blockDivide();// 打印:block4 – 0
}
是不是简单了呢。
block作为对象属性
block作为对象的属性可以存储该对象想要做的一些操作。当调用该对象的时候,对象的block就执行这些操作。
看下面的例子:
新建一个HXPerson类,当点击控制器view的时候,就让对象执行之前存储在block中的操作。HXPerson.h
#import <Foundation/Foundation.h>
typedef void (^BlockType)();
@interface HXPerson : NSObject
@property (nonatomic, copy) BlockType printSomething;// block类型属性
@end
HXPerson.m文件中什么都没写,我们主要看block的功能。
现在控制器中使用HXPerson类。
- (void)viewDidLoad {
[super viewDidLoad];
HXPerson *person = [[HXPerson alloc] init];
person.age = 20;
// 定义person对象的block属性(先让person的block存储一些操作)
person.printSomething = ^(){
NSLog(@"person");
};
self.person = person;
}
/**
* 当点击控制器view的时候,就让person执行之前存储在block中的操作。
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 调用person的block
self.person.printSomething();
}
运行结果:
block传值
利用block可以存储操作的这个特点,我们还可以使用block进行页面之间的传值,之前已经介绍过两种数据传递的方法,今天介绍第三种数据传递的方法:block传值
例子:两个控制器A、B,现在要将B中的数据传递给A
首先创建两个控制器:
要想把NextViewController的数据传递出来,就要在该控制器中定义block来传递
NextViewController.h
#import <UIKit/UIKit.h>
typedef void (^MyBlock)(NSString *text);
@interface NextViewController : UIViewController
@property (nonatomic, copy) MyBlock block;
@end
点击ViewController上的按钮,跳转到NextViewController。跳转前在NextViewController的block属性 中存储操作。
ViewController.m
- (IBAction)next {
// 从storyboard中加载控制器(之前介绍过控制器的创建)
UIStoryboard *st = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
NextViewController *vc = [st instantiateViewControllerWithIdentifier:@"next"];
// 在跳转的时候,先在block属性中存储操作(将传过来的字符串,赋值给label)
vc.block = ^(NSString *text){
self.label.text = text;
};
[self presentViewController:vc animated:YES completion:nil];
}
跳转到NextViewController控制器。
NextViewController.m
- (IBAction)previous {
// 在返回上一页面的时候,调用block,执行之前存储的操作
if (self.block) {
self.block(self.textField.text);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
运行结果:
block传递只能逆传。不能顺传。即如果A跳转到B,则使用block只能将B的数据传递的A。
之后的文章将介绍关于block的内存管理问题。