Block使用场景,可以在两个界面的传值,也可以对代码封装作为参数的传递等。用过GCD就知道Block的精妙之处。
Block简介
Block是一种比较特殊的数据类型。
它可以保存一段代码,在合适的时候取出来调用。
举个🌰:无参数无返回值的Block
无参数无返回值的Block
/** * 无参数无返回值的Block */
-(void)func1{
/** * void :就是无返回值
* emptyBlock:就是该block的名字
* ():这里相当于放参数。由于这里是无参数,所以就什么都不写 */
void (^emptyBlock)() = ^() {
NSLog(@"无参数,无返回值的Block");
};
emptyBlock();
}
🌰2:Block结合typedef使用
自己定义一个Block类型,用定义的类型去创建Block,更加简单便捷。
这里举例一个Block回调修改上一下界面的背景颜色。
ViewController1 控制器1,
ViewController2 控制器2控制器1跳转到控制器2,
然后在控制器2触发事件回调修改控制器1的背景颜色为红色。
ViewController2的实现
#import <UIKit/UIKit.h>
@interface ViewController2 : UIViewController
/** * 定义了一个changeColor的Block。这个changeColor必须带一个参数,这个参数的类型必须为id类型的
* 无返回值
* @param id */
typedef void(^changeColor)(id);
/**
* 用上面定义的changeColor声明一个Block,声明的这个Block必须遵守声明的要求。 */
@property (nonatomic, copy) changeColor backgroundColor;
@end
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//声明一个颜色
UIColor *color = [UIColor redColor];
//用刚刚声明的那个Block去回调修改上一界面的背景
self.backgroundColor(color);
}
ViewController1的实现
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ViewController2 *vc =[[ViewController2 alloc]init];
// 回调修改颜色
vc.backgroundColor = ^(UIColor *color){
self.view.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];}
关于block的传值
Block是什么?用一句话来概括就是带有自动变量的匿名函数。
只要用__block定义的变量a, a就是可以在匿名函数中被改变,比如
__block int multiplier = 7;
int multiplier2 = 7;
int (^myBlock)(int) = ^(int num)
{
multiplier ++;//这样就可以了
multiplier2 = 4;
return num * multiplier;
};
代码输出:
multiplier = 8
multiplier2 = 7
multiplier 被改变了, multiplier2没有被改变;
用__block定义变量multiplier,说明它是可以改变的。
在学习使用块编写的过程中,难点之一是掌握块的语法。本段内容详解介绍块语法,并且会通过示例展示块在代码中的作用。 块类型由返回值类型和参数类型列表构成。使用脱字符(^)可以声明块类型的变量。
回调消息:
观察者self, 在收到名为 @"NOTIFICATION_NAME"
的事件是执行 @selector(execute:),最后一个参数是表示会对哪个发送者对象发出的事件作出响应,nil 时表示接受所有发送者的事件。
[[NSNotificationCenter defaultCenter] postNotificationName:@"NOTIFICATION_NAME" object:self];
另外,如果需要增加参数,则使用另一个实例:userinfo
首先,我们在需要接收通知的地方注册观察者,比如:
//获取通知中心单例对象
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
//添加当前类对象为一个观察者,name和object设置为nil,表示接收一切通知
[center addObserver:self selector:@selector(notice:) name:@"123" object:nil];
之后,在我们需要时发送通知消息
//创建一个消息对象
NSNotification * notice = [NSNotification notificationWithName:@"123" object:nil userInfo:@{@"1":@"123"}];
//发送消息
[[NSNotificationCenter defaultCenter]postNotification:notice];
我们可以在回调的函数中取到userInfo内容,如下:
-(void)notice:(id)sender{ NSLog(@"%@",sender);}
打印结果如下:
再看一个🌰, 保证队列的串行运行,同时要保证一些操作是同步阻塞的。
- (void)addSession:(id)asession{
dispatch_block_t addSessionBlock = ^{
YFVideoRecorderKit* recodeSession = (YFVideoRecorderKit *)(asession);
[self addKit:recodeSession];
__weak YFSessionManager *wself = self;
__weak YFVideoRecorderKit *wkit = recodeSession;
//如果开启了录制,监控gpuimage movieWriter的录制结束回调
recodeSession.movieWriter.completionBlock = ^{
dispatch_sync(_manager_q, ^{
[wself removeKit:wkit]; //要用同步或者加锁的方式
});
};
//如果没开录制(初始化和预览状态),监控VideoRecodekit的回调
recodeSession.finishBlock = ^{
dispatch_sync(_manager_q, ^{
[wself removeKit:wkit]; //要用同步或者加锁的方式
});
};
};
if (dispatch_get_specific(self.queueManagerKey)) {
//说明当前的线程队列就是queue
addSessionBlock();
}else{
//说明当前的线程队列不是queue, 同步执行
dispatch_sync(_manager_q, ^{
addSessionBlock();
});
}
}
如何在 block 中修改外部变量
** 考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字 __block 修饰的变量。请看:**
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
__block int blockLocal = 100;
static int staticLocal = 100;
void (^aBlock)(void) = ^(void){
NSLog(@" >> Sum: %d\n", global + staticLocal);
global++;
blockLocal++;
staticLocal++;
};
aBlock();
NSLog(@"After modified, global: %d, block local: %d, static local: %d\n", global, blockLocal, staticLocal);
[pool drain];
执行之后,值均为:101