iOS设计模式编程思想

ReactiveCocoa2.5基础知识

2017-09-15  本文已影响52人  TiDown

ReactiveCocoa2.5基础知识

[toc]

ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,本文中简称RAC。在开发 GitHub for Mac 过程中的一个副产品,它提供了一系列用来组合和转换值流的 API 。本文只是记录自己学到ReactiveCocoa的笔记。

ReactiveCocoa核心组件

信号源

RACStream 有两个派生类

RACSequence :基于空间的数据流(在RAC中很少使用)
创建
    RACSequence *sequence1 = [RACSequence return:@1];
    NSLog(@"%@",sequence1.head);
    // 输出:1
    RACSequence *sequence2 = [RACSequence sequenceWithHeadBlock:^id{
        return @2;
    } tailBlock:^RACSequence *{
        return sequence1;
    }];
    NSLog(@"%@ %@",sequence2.head,sequence2.tail.head);
    // 输出:2 1
    RACSequence *sequence3 = @[@4,@5,@6,@7].rac_sequence;
     for (id value in concatSquence) {
          NSLog(@"%@",value);
    }
    // 输出:4
    // 输出:5
    // 输出:6
    // 输出:7
变换
    RACSequence *mapSrquence = [sequence1 map:^id(NSNumber *value) {
            return @(value.integerValue *3); // 1 * 3
        }];
        NSLog(@"%@",mapSrquence.head);
        // 输出:3
// concat:合并,目的:有两部分数据,想让上部分先执行,完了之后再让下部分执行(都可获取值)
    RACSequence *concatSquence = [sequence2 concat:mapSrquence];
    for (id value in concatSquence) {
        NSLog(@"%@",value);
    }
    // 输出:2
    // 输出:1
    // 输出:3

    concatSquence: 2 1 3
    sequence3    : 4 5 6 7
    RACSequence *zipSquence = [RACSequence zip:@[concatSquence,sequence3]];
    
遍历
for (RACTuple *tuple in zipSquence) {
        NSLog(@"%@---%@",tuple.first,tuple.second);
    }
    // 输出:2---4
    // 输出:1---5
    // 输出:3---6
RACSignal :基于时间的数据流
1.使用基础
- 单元信号
    RACSignal *signal1 = [RACSignal return:@1];
    RACSignal *signal2 = [RACSignal error:errorObject];
    RACSignal *signal3 = [RACSignal empty];
    RACSignal *signal4 = [RACSignal never];
- 动态信号
    RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        // 如果出现错误,sendCompleted就会失效
//        [subscriber sendError:errorObject];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
        }];
    }];
- Cocoa桥接
    // 监听
    RACSignal *signal6 = [view rac_signalForSelector:@selector(setFrame)];
    // 事件点击
    RACSignal *signal7 = [button rac_signalForControlEvents:UIControlEventTouchUpInside];
    // dealloc
    RACSignal *signal8 = [view rac_willDeallocSignal];
    // KVO
    RACSignal *signal9 = RACObserve(view, backgroundColor);
- 信号变换
    RACSignal *signal10 = [signal1 map:^id(NSString *value) {
        return [value substringFromIndex:1];
    }];
- 序列转换
  RACSequence *sequence = [RACSequence return:@1];
    RACSignal *signal11 = sequence.signal;
2.订阅一个信号的方式

2.1 ) 订阅方法

// 有三种参数方法
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock;
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock;
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;
 example:
 [signal11 subscribeNext:^(id x) {
        NSLog(@"%@",x);
    } error:^(NSError *error) {
        NSLog(@"error");
    } completed:^{
        NSLog(@"finished");
    }];

2.2 ) 绑定

RAC(view,backgroundColor) = signal10;

2.3 ) Cocoa桥接

// rac_liftSelector: 相当于多线程组;所有信号请求完成之后再执行方法,只需把三个结果传出去即可
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignals:signal1,signal3, nil];
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[signal1,signal1]];
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalOfArguments:signal1];
3.单个信号的变换

3.1 ) 值操作

   RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       [subscriber sendNext:@1];
       [subscriber sendNext:@2];
       [subscriber sendNext:@3];
       [subscriber sendCompleted];
       return nil;
   }];
   RACSignal *signalB = [signalA map:^id(NSNumber *value) {
       return @(value.integerValue * 3);
   }];
   [signalB subscribeNext:^(id x) {
       NSLog(@"%@",x);
   }];
   // 输出:3
   // 输出:6
   // 输出:9
RACSignal *signalC = [signalA mapReplace:@8];
   [signalC subscribeNext:^(id x) {
       NSLog(@"%@",x);
   }];
   // 输出:8
   // 输出:8
   // 输出:8
RACSignal *signalD = [signalA reduceEach:^id(NSNumber *num1 , NSNumber *num2){
        return @([num1 intValue] + [num2 intValue]);
    }];
- (RACSignal *)not {
    return [[self map:^(NSNumber *value) {
        NSCAssert([value isKindOfClass:NSNumber.class], @"-not must only be used on a signal of NSNumbers. Instead, got: %@", value);
        return @(!value.boolValue);
    }] setNameWithFormat:@"[%@] -not", self.name];
}
- (RACSignal *)and {
    return [[self map:^(RACTuple *tuple) {
        NSCAssert([tuple isKindOfClass:RACTuple.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
        NSCAssert(tuple.count > 0, @"-and must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
        return @([tuple.rac_sequence all:^(NSNumber *number) {
            NSCAssert([number isKindOfClass:NSNumber.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
            return number.boolValue;
        }]);
    }] setNameWithFormat:@"[%@] -and", self.name];
}
- (RACSignal *)or {
    return [[self map:^(RACTuple *tuple) {
        NSCAssert([tuple isKindOfClass:RACTuple.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
        NSCAssert(tuple.count > 0, @"-or must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
        return @([tuple.rac_sequence any:^(NSNumber *number) {
            NSCAssert([number isKindOfClass:NSNumber.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
            return number.boolValue;
        }]);
    }] setNameWithFormat:@"[%@] -or", self.name];
}
- (RACSignal *)reduceApply {
    return [[self map:^(RACTuple *tuple) {
        NSCAssert([tuple isKindOfClass:RACTuple.class], @"-reduceApply must only be used on a signal of RACTuples. Instead, received: %@", tuple);
        NSCAssert(tuple.count > 1, @"-reduceApply must only be used on a signal of RACTuples, with at least a block in tuple[0] and its first argument in tuple[1]");
        // We can't use -array, because we need to preserve RACTupleNil
        NSMutableArray *tupleArray = [NSMutableArray arrayWithCapacity:tuple.count];
        for (id val in tuple) {
            [tupleArray addObject:val];
        }
        RACTuple *arguments = [RACTuple tupleWithObjectsFromArray:[tupleArray subarrayWithRange:NSMakeRange(1, tupleArray.count - 1)]];
        return [RACBlockTrampoline invokeBlock:tuple[0] withArguments:arguments];
    }] setNameWithFormat:@"[%@] -reduceApply", self.name];
}
// 举个例子
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
                              id block = ^id(NSNumber *first,NSNumber *second,NSNumber *third) {
                                  return @(first.integerValue + second.integerValue * third.integerValue);
                              };
                              [subscriber sendNext:RACTuplePack(block,@1 , @3 , @5)];
                              [subscriber sendNext:RACTuplePack((id)(^id(NSNumber *x){return @(x.intValue * 10);}),@10,@20,@30)];
                              [subscriber sendCompleted];
                              return [RACDisposable disposableWithBlock:^{
                              }];
                          }];
    RACSignal *signalB = [signalA reduceApply];
    [signalB subscribeNext:^(id x) {
                NSLog(@"%@",x);
            }];
    // 输出:16 = 1 + 3*5
    // 输出:100 = 10 * 10
- (RACSignal *)materialize {
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        return [self subscribeNext:^(id x) {
            [subscriber sendNext:[RACEvent eventWithValue:x]];
        } error:^(NSError *error) {
            [subscriber sendNext:[RACEvent eventWithError:error]];
            [subscriber sendCompleted];
        } completed:^{
            [subscriber sendNext:RACEvent.completedEvent];
            [subscriber sendCompleted];
        }];
    }] setNameWithFormat:@"[%@] -materialize", self.name];
}
- (RACSignal *)dematerialize {
    return [[self bind:^{
        return ^(RACEvent *event, BOOL *stop) {
            switch (event.eventType) {
                case RACEventTypeCompleted:
                    *stop = YES;
                    return [RACSignal empty];
                case RACEventTypeError:
                    *stop = YES;
                    return [RACSignal error:event.error];
                case RACEventTypeNext:
                    return [RACSignal return:event.value];
            }
        };
    }] setNameWithFormat:@"[%@] -dematerialize", self.name];
}

3.2 ) 数量操作


RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"ab"];
        [subscriber sendNext:@"hello"];
        [subscriber sendNext:@"world"];
        [subscriber sendNext:@"123"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *signalC = [signalA filter:^BOOL(NSString *value) {
        return value.length > 3;
    }];
    [signalC subscribeNext:^(id x) { 
        NSLog(@"%@",x);
    }];
// 输出:hello
// 输出:world
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"Ab"];
        [subscriber sendNext:@"hello"];
        [subscriber sendNext:@"world"];
        [subscriber sendNext:@"123"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *signalB = [signalA filter:^BOOL(NSString *value) {
        return ![value isEqual:@"ab"];
    }];
    [signalB subscribeNext:^(id x) { 
        NSLog(@"%@",x);
    }];
// 输出:hello
// 输出:world
// 输出:123
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"Ab"];
        [subscriber sendNext:@"hello"];
        [subscriber sendNext:@"world"];
        [subscriber sendNext:@"123"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *signalC = [signalA ignore:@"ab"];
    [signalC subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
// 输出:hello
// 输出:world
// 输出:123
- (RACSignal *)doError:(void (^)(NSError *error))block;
- (RACSignal *)doCompleted:(void (^)(void))block;
- (RACSignal *)initially:(void (^)(void))block;
- (RACSignal *)finally:(void (^)(void))block;
RACSignal *signalC = [signalA merge:signalB];
RACSignal *signalD = [RACSignal merge:@[signalA,signalB]];
RACSignal *signalE = [RACSignal merge:RACTuplePack(signalA,signalB)];
RACSignal *signalC = [signalA zipWith:signalB];
RACSignal *signalD = [RACSignal zip:@[signalA,signalB]];
RACSignal *signalE = [RACSignal zip:RACTuplePack(signalA,signalB)];
RACSignal *signalC = [signalA combineLatestWith:signalB];
RACSignal *signalD = [RACSignal combineLatest:@[signalA,signalB]];
两者的区别
Pull-driver vs Push-driver

例子:看电影
Sequence:看本地下载的电影,突然想上厕所,可以暂停,上完厕所回来再看(观看者决定)
Signal:在线看电影,不管你看不看电影一直都在播放(发送者决定)

Data vs Event

Sequence: 任何ID类型的数据(值)
Signal:基于信号(值,错误,终止,中断)

订阅者

RACSubscriber

订阅过程

对于订阅过程不了解可以查看这三篇博客
细说ReactiveCocoa的冷信号与热信号(一)
细说ReactiveCocoa的冷信号与热信号(二)
细说ReactiveCocoa的冷信号与热信号(三)

RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"dispose");
        }];
    }];
    RACDisposable *disposable = [signal5 subscribeNext:^(id x) {
        NSLog(@"%@",x);
    } error:^(NSError *error) {
        NSLog(@"error");
    } completed:^{
        NSLog(@"finished");
    }];
    [disposable dispose];

调度器

RACScheduler

RACSchedulerReactiveCocoa 中就是扮演着调度器的角色,本质上,它就是用 GCD 的串行队列来实现的,并且支持取消操作。是的,在 ReactiveCocoa 中,并没有使用到 NSOperationQueueNSRunloop 等技术,RACScheduler 也只是对 GCD 的简单封装而已。

清洁工

RACDisposable

RACDisposableReactiveCocoa 中就充当着清洁工的角色,它封装了取消和清理一次订阅所必需的工作。它有一个核心的方法 -dispose ,调用这个方法就会执行相应的清理工作,这有点类似于 NSObject-dealloc 方法

上一篇 下一篇

猜你喜欢

热点阅读