来做个block的笔记。
- block定义
- block类型
- block传值
- block作为参数的方法定义与调用
- block作为返回值
(如果嫌文章太长想直接看代码的可以点这里block练习总结demo)
block定义
block:带有自动变量(局部变量)的匿名函数。
在xcode里直接输入inline
就会出现block的具体书写格式
block格式:返回值类型(^block的名字)(参数类型) = ^(参数类型和参数名) {};
returnType(^blockName)(parameterTypes) = ^(parameters) { statements };
格式里的返回值可以为空、参数类型可以为空、参数类型和参数名也可以为空。
block类型
- 1.没有返回值,没有参数的block
// 没有返回值,没有参数的定义方式
void(^myBlock_1)() = ^{
NSLog(@"mymyBlock_111");
};
myBlock_1(); // 调用block
->运行结果 “mymyBlock_111”
- 2.没有返回值,有参数的block
// 没有返回值,两个参数为int,必须要写参数,而且必须要有参数变量名
void(^myBlock_2)(int, int) = ^(int a, int b){
int c = a + b;
NSLog(@" a + b = %d", c);
};
myBlock_2(2 ,3); // 调用
// ->运行结果“a + b = 5”
- 3.带返回值、参数的block
// 带有返回值与参数 返回值int 参数为int
int(^myBlock_3)(int) = ^(int d){
return d * d;
};
int result = myBlock_3(6);
NSLog(@" 运算结果 = %d",result);
// ->运行结果“运算结果 = 36”
- 4.定义block类型
定义block的类型:
格式就是 返回值(^)(参数类型)
typedef void(^wxkBlock)(NSString * valueStr); // wxkBlock不是变量名,而是这种类型的block的别名
@interface ViewController : UIViewController
@property (nonatomic, copy) wxkBlock wBlcok; //由上面wxkBlock创建,这样在类中可以拿到self.wBlcok
@end
//也可以这样定义
@interface ViewController : UIViewController
@property (nonatomic, copy) void(^xkBlock)(NSString * valueStr); //这样在类中可以拿到self.xkBlock
@end
block传值
第一个控制器为ViewController
第二个控制器为BlockPassByValueVC
我们从 ViewController 跳入 BlockPassByValueVC ,然后从BlockPassByValueVC 跳回第一个页面,顺便传个值给 ViewController (反向传值)
在ViewController 打印传过来的值
// 在 BlockPassByValueVC 的.h文件里定义block(和使用delegate差不多)
#import <UIKit/UIKit.h>
@interface BlockPassByValueVC : UIViewController
// 反向传值的block
@property (nonatomic, copy) void(^passValueBlock)(NSString * valueStr);
@end
// 从ViewController的.m 跳进 BlockPassByValueVC 的点击事件
- (IBAction)exchangeValue:(id)sender
{
BlockPassByValueVC * BlockPVc = [[BlockPassByValueVC alloc] init];
/**
passValueBlock 是 BlockPVc 这样控制器里的 block
valueStr 为 BlockPVc 控制器要传递过来的参数
{}括号里的代码是在BlockPVc的.m文件里传了参数运行block的时候才会调用
*/
BlockPVc.passValueBlock = ^(NSString *valueStr) {
NSLog(@"传过来的值 = %@",valueStr);
};
[self presentViewController:BlockPVc animated:YES completion:nil];
}
// BlockPassByValueVC 的.m文件里传值的点击事件
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (self.passValueBlock) // 如果在上一个页面调用了这个block,就执行下面的方法
{
self.passValueBlock(@"10086"); //把这里的值当参数传递过去
}
[self dismissViewControllerAnimated:YES completion:nil];
}
// 在跳回ViewController 的时候 传递的值"10086"就会传到ViewController里
ViewController的输出的结果为: 传过来的值 = 10086
如果没看明白的话可以看一下我总结的block练习总结demo
block作为参数的方法定义与调用
- block作为参数 无返回值 无参数(事件完成后调用block块里的方法)
// 定义 在这个方法完成后 再调用其他方法(写在block里的方法)
- (void)block_t_5:(void(^)())finish
{
NSLog(@"先处理block_t_5方法里的事件");
// 在调用完此方法处理事件后 调用block块里的方法
if (finish)
{
finish();
}
}
// 调用方法
[self block_t_5:^{
NSLog(@"完成block_t_5事件成功后,在进行其他操作");
}];
// 方法调用后打印的日志
// log:先处理block_t_5方法里的事件
// log:完成block_t_5事件成功后,在进行其他操作
- block作为参数 无返回值 参数为NSSstring类型
// 定义 无返回值 参数为NSSstring类型
- (void)block_t_6:(void(^)(NSString * B_str)) finish
{
NSLog(@"先处理block_t_6方法里的事件");
if (finish)
{
NSString * tempStr = @"block_t_6-10086";
finish(tempStr);
}
}
// 调用方法
[self block_t_6:^(NSString *B_str) {
NSLog(@"完成block_t_6事件成功后--%@",B_str);
}];
// 方法调用后打印的日志
// log:先处理block_t_6方法里的事件
// log:完成block_t_6事件成功后--block_t_6-10086
- block作为参数 无返回值 参数为NSSstring类型,根据另一个方法参数code的值来判断执行不同的block
// 定义 无返回值 参数为NSSstring类型,根据另一个方法参数code的值来判断执行不同的block
- (void)block_t_7:(void(^)(NSString * B_str7))myBlock_7 AndCodeName:(int)code
{
switch (code) {
case 1:
{
NSLog(@"code等于111时的操作");
if (myBlock_7)
{
myBlock_7(@"11111111");
}
}
break;
case 2:
{
NSLog(@"code等于222时的操作");
if (myBlock_7)
{
myBlock_7(@"222222222");
}
}
break;
default:
{
if (myBlock_7)
{
myBlock_7(nil);
}
}
break;
}
}
// 调用方法
[self block_t_7:^(NSString *B_str7)
{
NSLog(@" -- %@",B_str7);
}AndCodeName:1]; // 传入方法的code值为1
// 方法调用后打印的日志
// log:code等于111时的操作
// log:11111111
- block作为参数 返回值为NSString类型 参数为NSString类型
// 定义 返回值为NSString类型 参数为NSString类型
- (void)block_t_8:(NSString *(^)(NSString * B_str8))myBlock_8
{
//
NSString * tempStr = @"T_str";
if (myBlock_8)
{
tempStr = myBlock_8(tempStr);
NSLog(@"tempStr = %@",tempStr);
}
}
// 调用方法
[self block_t_8:^NSString *(NSString *B_str8) {
return [NSString stringWithFormat:@"拼接一句话在传进的参数前 - %@",B_str8];
}];
// 方法调用后打印的日志
// log:tempStr = 拼接一句话在传进的参数前 - T_str
如果没看明白 可以看一下我总结的block练习总结demo
block作为返回值
参考网上都是以类似计算器数值相加来实现block做返回值的链式编程,类似 Masonry 的调用那样。
一个相加的工具类 MyTool
一个NSObject的类别 NSObject+Tool 定义个类方法 方便调用(也许现在你还不懂为什么创建者两个类,看代码你就明白了)
MyTool类
// MyTool的 .h
@interface MyTool : NSObject
@property (nonatomic, assign) NSInteger result; // 相加的结果
// 返回值为block的方法,block的返回值为这个类的本身,参数为NSInteger
- (MyTool * (^)(NSInteger)) add;
@end
// MyTool的 .m
@implementation MyTool
- (MyTool * (^)(NSInteger)) add
{
// 返回值为 block
return ^(NSInteger value){
_result += value;
return self; // block的返回值为它自己本身(为了后面可以持续用“点语法”调用)
};
}
@end
NSObject+Tool类
NSObject+Tool的.h
@interface NSObject (Tool)
// block做参数的类方法 block的返回值为空 参数为MyTool
+ (NSInteger)wxk_makeTool:(void(^)(MyTool *))block;
@end
NSObject+Tool的.m
@implementation NSObject (Tool)
+ (NSInteger)wxk_makeTool:(void (^)(MyTool *))block
{
MyTool * myTool = [[MyTool alloc] init];
block(myTool); // 调用block MyTool本身作为该block的参数
return myTool.result; // 返回运算结果
}
@end
方法调用
#import "MyTool.h"
#import "NSObject+Tool.h"
- (void)viewDidLoad {
[super viewDidLoad];
// 链式变成 block做参数 做返回值
NSInteger values = [NSObject wxk_makeTool:^(MyTool * tool) {
tool.add(10).add(20).add(30);
}];
NSLog(@"values = %ld",(long)values);
}
// log:values = 60
最后这个“block作为返回值” 说的比较模糊,会再更新做调整。
如果没看明白 可以看一下我总结的block练习总结demo
- 之前我在写block的时候 顺手就把它的修饰写成了
strong
,关于block要用copy修饰,还是用strong修饰.
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。