本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java
, 数据结构与算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 联系微信tsaievan
.
接着说RAC的常见用法 :
这是今天要说的几个常见用法:
-
rac_liftSelector
- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... NS_REQUIRES_NIL_TERMINATION;
当多个信号都被发送时, 调用selector方法:
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[subscriber sendNext:data];
}] resume];
return nil;
}];
RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest1"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[subscriber sendNext:data];
}] resume];
return nil;
}];
[self rac_liftSelector:@selector(updateUIWithData1:data2:) withSignals:signal1, signal2, nil];
}
- (void)updateUIWithData1:(NSData *)data1 data2:(NSData *)data2 {
/* 主线程更新UI */
dispatch_async(dispatch_get_main_queue(), ^{
NSString *string1 = [[NSString alloc] initWithData:data1 encoding:NSUTF8StringEncoding];
NSString *string2 = [[NSString alloc] initWithData:data2 encoding:NSUTF8StringEncoding];
NSString *string = [string1 stringByAppendingString:string2];
NSLog(@"%@---%@", string, [NSThread currentThread]);
});
}
设想一个场景, 当发起两个网络请求, 这两个网络请求都必须拿到数据了之后才能更新UI, 有点类似于调度组的使用. 那么就可以使用上面那一串代码.
-
多播连接
当我创建了一个信号, 信号在多处被订阅, 然后在信号发送的时候, 有多处要执行block的时候, 就可以使用多播连接. 这个时候, 使用多播连接, 创建信号的block就只会执行一次.
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *sourceSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[subscriber sendNext:data];
}] resume];
return nil;
}];
RACMulticastConnection *connection = [sourceSignal publish];
[connection.signal subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"A处调用了数据:%@", dataString);
}];
[connection.signal subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"B处调用了数据:%@", dataString);
}];
[connection connect];
}
- 先将信号转化成
connection
:
RACMulticastConnection *connection = [sourceSignal publish];
在publish
方法中, 先创建了一个subject
对象, subject
既可以发送信号, 也可以订阅信号.
在这个方法中, 将初始创建的信号保存至sourceSignal
属性中, 将subject
保存至signal
属性中.
2.两处订阅信号, 这个地方并不是用sourceSignal
来订阅的, 而是用connect
的signal
属性, 这个属性就是subject
.
[connection.signal subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"A处调用了数据:%@", dataString);
}];
[connection.signal subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"B处调用了数据:%@", dataString);
}];
subject
订阅信号就做了两件事情, 一是创建RACSubscriber
类对象o
, 二是订阅o
.
订阅o
就是将创建的o
都添加到可变数组中去, 然后这个可变数组是调用者subject
的subscribers
属性.
这样, connection
的signal
属性中就保存了一个subscribers
数组.
3.连接
[connection connect];
这里调用subscriber
方法就是调用创建sourceSignal
的block, 并将subscriber
作为参数传进去, 所以:
所以, 相当于是:
[connection.signal sendNext: data];
subject
调用sendNext:
方法过程如下:
会遍历self.subscriber
, 然后一一执行block, 这样就做到了"多播"的功能
-
RACCommand
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
NSLog(@"%@", input);
return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[subscriber sendNext:data];
}];
return nil;
}];
}];
[command execute:@"执行命令行"];
那么, 我如何拿到subscriber
发送的data
呢?
[command execute:@"执行命令行"]
这句代码的返回值是RACSignal
, 所以可以订阅:
RACSignal *signal = [command execute:@"执行命令行"];
[signal subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"%@", dataString);
}];
我们还有另外一种方法可以拿到data:
command
中有一个属性叫做executionSignals
, 这个被称为是信号源->signal of signal
[command.executionSignals subscribeNext:^(id _Nullable x) {
}];
所以这个x
就是上面command
在创建过程中返回的signal, 然后我将signal拿到之后再次订阅, 就可以拿到发送的data值了:
[command.executionSignals subscribeNext:^(RACSignal *x) {
[x subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"result is : %@", dataString);
}];
}];
[command execute:@"执行命令"];
还有一种更简单的方法: 由于信号源此时只发送了一个信号,那我我们直接拿信号源发送的最新的信号进行订阅即可:
[command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"result is : %@", dataString);
}];
[command execute:@"执行命令行"];
除此之外, command
还可以监控命令执行的情况 :
[command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSString *dataString = [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding];
NSLog(@"result is : %@", dataString);
}];
[command.executing subscribeNext:^(NSNumber * _Nullable x) {
if ([x boolValue]) {
NSLog(@"正在执行");
}else {
NSLog(@"执行结束 或者 尚未开始执行");
}
}];
[command execute:@"执行命令行"];
没有执行只有两种情况:
1.执行尚未开始
2.执行已经结束
但是执行结束后却没有在执行:
NSLog(@"执行结束 或者 尚未开始执行");
这句代码. 这是因为我们没有一个信号发送结束的指令, 当我们加了这句代码之后, 就可以了:
[subscriber sendCompleted];
-
bind
- (void)viewDidLoad {
[super viewDidLoad];
RACSubject *subject = [RACSubject subject];
self.bindSignal = [subject bind:^RACSignalBindBlock _Nonnull{
return ^RACSignal *(NSMutableArray *value, BOOL *stop) {
value = [[value.rac_sequence map:^id _Nullable(NSDictionary *value) {
return [KFCFood kfcFoodWithDictionary:value];
}] array].mutableCopy;
return [RACReturnSignal return:value];
};
}];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSArray *infoArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL];
[subject sendNext:infoArray.mutableCopy];
}] resume];
});
[self updateUI];
}
- (void)updateUI {
[_bindSignal subscribeNext:^(id _Nullable x) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"拿到模型数据: %@ ----线程 : %@ ", x, [NSThread currentThread]);
});
}];
}
创建一个subject
, 然后绑定一个block, 这个block 是一个有返回值的block, 他的返回值又是一个block, RACSignalBindBlock
类型的block, 然后这个block也有返回值, 返回一个信号. 我们返回什么信号呢?
返回的信号可以将参数value
值进行修饰,然后返回出去, 在这个过程中, 我们可以进行字典转模型 :
这个value是何时得到的呢? 就是subject发送信号的时候的数据, 当我们进行网络下载时, 拿到的json, 转换成字典, 然后再发送出去.
value = [[value.rac_sequence map:^id _Nullable(NSDictionary *value) {
return [KFCFood kfcFoodWithDictionary:value];
}] array].mutableCopy;
return [RACReturnSignal return:value];
当我们用绑定信号进行订阅的时候, 就能够拿到return回来的value, 即模型数组.
以上!
本文中涉及到的代码 密码: ruur