函数响应式编程及ReactiveObjC学习笔记 (-)

最近无心间看到一个视频讲的ReactiveObjC, 以为挺好用的 但听完后只是了解个大概.react

在网上找了些文章, 有的写的比较易懂但看完仍是没以为本身能比较好的使用RAC, 有的甚至让我看不下去面试

 

这两天恰好公司项目交付闲下来, 想本身去啃下官方文档编程

ReactiveCocoa是一个基于函数响应式编程的OC框架.框架

那么什么是函数式响应式编程呢?概念我就不讲了 由于我讲的也不必定准确, 你们能够去baidu看看大神们的解释函数

下面我大概演示下响应式编程的样子ui

Masonry是比较常见的一个响应式框架, 它的的用法举例以下:spa

make.centerY.equalTo(self.view).offset(100);

你们注意它的用法, 点号调用一个事件或属性后能够接着点号调用, 这里一个比较明显的函数响应式编程的好处就是咱们能够把一些要使用的连贯的或者有前后顺序的调用方法和事件连在一块儿, 逻辑清晰明了的完成代码.线程

那么要如何实现这样的调用方式呢?代理

centerY.equalTo(self.view)这个能执行的话equalTo就必须是一个返回对象的blockcode

下面试试本身来实现这个, 

建一个Person对象,  加上跑步, 走路的方法

Class: Person;  Method: run; walk;

咱们拆分红几个步骤来作, 首先实现

[[person run] walk];先跑, 跑累了再走

要实现这样的调用的话, run就必须返回person, 为了还能继续接着这样调用walk也要返回person

好了, 思路就很清晰了, 咱们上代码

#import <Foundation/Foundation.h>

@interface Person : NSObject

- (Person *)run;
- (Person *)walk;

@end

 

#import "Person.h"

@implementation Person

- (Person *)run {
    
    NSLog(@"跑步");
    
    // 延时2s
    [NSThread sleepForTimeInterval:2];
    
    return self;
}
- (Person *)walk {
    
    NSLog(@"走路");
    
    // 延时2s
    [NSThread sleepForTimeInterval:2];
    
    return self;
}

@end

你们能够看到, 咱们run 跟 walk方法都会返回对象自己, 为了延时我加了个延迟2s

咱们调用试试

    // 建立对象
    Person *person = [[Person alloc] init];
    
    // 尝试调用
    [[person run] walk];

结果以下:

2017-07-21 21:59:30.962 RAC[63284:11390973] 跑步
2017-07-21 21:59:33.036 RAC[63284:11390973] 走路

跟预期一致, 咱们再来实现person.run().walk();

刚才说了要返回一个返回值是对象的block, 咱们来实现下 代码以下:

- (Person * (^)())runBlock {
    
    Person * (^block)() = ^() {
        
        NSLog(@"run");
        // 延时2s
        [NSThread sleepForTimeInterval:2];
        return self;
    };
    
    return block;
}

- (Person * (^)())walkBlock {
    
    Person * (^block)() = ^() {
        
        NSLog(@"walk");
        // 延时2s
        [NSThread sleepForTimeInterval:2];
        return self;
    };
    
    return block;
}

若是对block使用不熟的同窗能够花点时间琢磨下block的结构

咱们调用试试看

    // 调用block
    person.runBlock().walkBlock();

结果:

2017-07-22 13:58:01.306 RAC[64288:11757631] run
2017-07-22 13:58:03.381 RAC[64288:11757631] walk

好了, 这样咱们就本身实现了一个基于函数响应式的小Demo

 

常规状况下, 咱们写代码是通常是定义不少个变量和方法,  在不一样的状态和业务流程下去改变变量的值或者调用对应的方法.

而RAC采用信号机制来获取当前的, 同时也能直接处理未来要如何修改这些值, 经过利用链式响应编程来书写结构逻辑清晰的代码, 不用咱们在不一样的地方去给咱们属性值作处理, 

 

好比咱们要给一个UITextField作监听, 当值改变的时候作一些处理例如打印当前输入的值, 常规用法下咱们要让当前控制器或者类遵循textField的代理, 而后把textField的代理指给当前类, 实现代理方法, 代码大概会是这样:

@interface ViewController ()<UITextFieldDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    

    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 35)];
    
    textField.center          = self.view.center;
    textField.backgroundColor = [UIColor yellowColor];
    textField.delegate        = self;
    
    [self.view addSubview:textField];
}

#pragma mark - UITextFieldDelegate Method

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    
    
    NSLog(@"%@", textField.text);
    return YES;
}

@end

或者你们也能用KVO来实现, 当代码比较少的时候这样看起来还比较清晰, 若是当时一个完整的项目呢, 那么多方法要写咱们要看看某一个textField事件估计要花一些时间在代码里面去找这个方法, 代码就不是很直观了.

 

那么RAC怎么帮助咱们解决这个问题呢,  上面有说过RAC是经过信号来管理的, 那么什么是信号呢?

RACSignal就是这个类, 咱们试试本身建立一个信号 首先咱们先用Pod导入ReactiveObjC库

pod 'ReactiveObjC', '~>3.0.0'

导入头文件

#import <ReactiveObjC.h>

咱们建立一个信号:

// 建立一个信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"建立一个信号"); return nil; }];

直接运行看看, 好像什么都没有发生, 怎么回事呢? 咱们点击建立新的方法看看他作了什么

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

他给咱们返回了一个RACDynamicSignal,  这个是什么呢? 咱们点他看看

@interface RACDynamicSignal : RACSignal

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;

原来他是RACSignal的一个子类, 它也重写了createSignal方法, 咱们如今实际是调用了他的建立信号的方法. 那咱们看看它这个方法都作了什么

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

它建立了一个RACDynamicSignal实例, 而后把didSubscribe复制了一份复制给建立的实例, 而后重命名后就直接返回给咱们了.

而后就结束了, 难怪咱们什么效果都没有看到

RAC里面有一个很重要的理念: 建立信号必须订阅, 订阅了信号才会被执行.  

没有订阅的信号是冷信号 不会产生任何效果, 订阅信号就从冷信号变成热信号, 就能够执行各类操做.

 

咱们看看如何订阅:

    // 建立一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"建立一个信号");
        return nil;
    }];
    
    // 订阅一个信号
    [signal subscribeNext:^(id  _Nullable x) {
        
        NSLog(@"订阅一个信号");
    }];

咱们运行看看

2017-07-22 15:05:58.760 RAC[65085:12004278] 建立一个信号

建立信号的block执行了,  可是订阅的信号没有执行, 咱们看看点开subscribeNext看看为何

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

它首先判断咱们的block不会空, 而后建立了一个RACSubscriber订阅者, 并把咱们的block给它了

再点subscriber的建立方法看看它作了什么

+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    RACSubscriber *subscriber = [[self alloc] init];

    subscriber->_next = [next copy];
    subscriber->_error = [error copy];
    subscriber->_completed = [completed copy];

    return subscriber;
}

它只是建立了一个subscriber实例, 而后把咱们的block拷贝给它 仍是什么都没有作

咱们再看看

[self subscribe:o];

作了什么

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCAssert(NO, @"This method must be overridden by subclasses");
    return nil;
}

它作了非空判断, 而后说这个方法必须被子类重写, 这里好像也啥都没干啊 怎么建立信号的block就执行了呢?

你们想一想, 咱们刚才建立信号的时候, 是否是就是调用的是RACSignal的子类DynamicSignal, 因此这里实际上运行的也是这个DynamicSignal的subscribe方法, 咱们去看看

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}

 

首先它也是先判断是否为空, 而后建立了一个RACCompoundDisposable实例

接着有给咱们的subscriber从新赋值, 咱们看看这个RACPassthroughSubscriber

// A private subscriber that passes through all events to another subscriber
// while not disposed.
@interface RACPassthroughSubscriber : NSObject <RACSubscriber>

它是把事件从一个subscriber传递给另一个subscriber, 因此这里就是它把咱们原有的subscriber + 以前建立的signal + disposable加起来组成一个新的subscriber从新赋值给咱们的subscriber,  至关于把咱们建立的信号跟订阅绑定到一块儿了

 

接着若是didsubscribe不为空的话, 及继续执行不然直接返回disposable

咱们的didsubscriber你们还记得是什么吗? 打印建立信号那段对吧

而后咱们看到它建立了一个RACDisposable实例, 可是它用的是一个RACScheduler来建立的

咱们看看这个RACScheduler是个啥

/// Schedulers are used to control when and where work is performed.
@interface RACScheduler : NSObject

哦 它是一个相似Timer或者dispatch_after的东西, 控制事件在何时触发

咱们再看看这个subscriptionScheduler

+ (RACScheduler *)subscriptionScheduler {
    static dispatch_once_t onceToken;
    static RACScheduler *subscriptionScheduler;
    dispatch_once(&onceToken, ^{
        subscriptionScheduler = [[RACSubscriptionScheduler alloc] init];
    });

    return subscriptionScheduler;
}

它建立了一个RACScheduler单例, 不过是用RACSubscriptionScheduler来初始化的, 咱们再看看它

@interface RACSubscriptionScheduler : RACScheduler

是一个RACSchedule的子类, 它重写的初始化和schedule , after...等等方法,  先记下一会看看是否用到了这些重写的方法

这里咱们先看看这个子类重写的初始化方法

- (instancetype)init {
    self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.subscriptionScheduler"];

    _backgroundScheduler = [RACScheduler scheduler];

    return self;
}

重命名, 而后给持有的一个RACScheduler对象backgroundScheduler赋值, 咱们看看RACScheduler的scheduler作了什么

+ (RACScheduler *)scheduler {
    return [self schedulerWithPriority:RACSchedulerPriorityDefault];
}

继续点

+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority {
    return [self schedulerWithPriority:priority name:@"org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler"];
}

仍是看不出来, 继续点

+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
    return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
}

返回了一个RACTargetQueueScheduler实例, targetQueue是一个dispatch_get_global_queue全局队列

/// A scheduler that enqueues blocks on a private serial queue, targeting an
/// arbitrary GCD queue.
@interface RACTargetQueueScheduler : RACQueueScheduler

/// Initializes the receiver with a serial queue that will target the given
/// `targetQueue`.
///
/// name        - The name of the scheduler. If nil, a default name will be used.
/// targetQueue - The queue to target. Cannot be NULL.
///
/// Returns the initialized object.
- (instancetype)initWithName:(nullable NSString *)name targetQueue:(dispatch_queue_t)targetQueue;

一个相似队列的东西, 看看它的初始化方法

- (instancetype)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue {
    NSCParameterAssert(targetQueue != NULL);

    if (name == nil) {
        name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)];
    }

    dispatch_queue_t queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL);
    if (queue == NULL) return nil;

    dispatch_set_target_queue(queue, targetQueue);

    return [super initWithName:name queue:queue];
}

前面很清晰, 建立了一个队列

看看super的初始化作了什么< 

- (instancetype)initWithName:(NSString *)name queue:(dispatch_queue_t)queue {
    NSCParameterAssert(queue != NULL);

    self = [super initWithName:name];

    _queue = queue;
#if !OS_OBJECT_USE_OBJC
    dispatch_retain(_queue);
#endif

    return self;
}

保存了这个队列, 就结束了

 

还记获得哪里了吗, 我把代码再贴下

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}

如今到这个schedule了, 咱们看看它作了什么, 注意哦这个时候要去看RACScheduler的子类RACSubscriptionScheduler中的方法

- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];

    block();
    return nil;
}

看到了吗 block(), 是否是历来没有以为这对括号这么可爱的, 咱们看着这么半天终于看到一个执行block的地方了

先不急, 咱们看看它以前的代码

首先判断block非空, 而后若是RACScheduler.currentScheduler为空的话, 就让backgroundscheduler去调用block

这个backgroundscheduler看菜咱们有看是一个RACScheduler的实例, 咱们先看看若是为空要怎么样

- (RACDisposable *)schedule:(void (^)(void))block {
    NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
    return nil;
}

啥都没干,  看起来这里咱们就要期待它必定不为空了,  否则咱们咱们辛辛苦苦找到一个执行的地方就又白找了

那么它究竟是不是空呢, 咱们先看看它的定义

/// The current scheduler. This will only be valid when used from within a
/// -[RACScheduler schedule:] block or when on the main thread.
+ (nullable RACScheduler *)currentScheduler;

说是只要在主线程, 或者调用过[RACScheduler schedule]方法就不为空

那么咱们如今是在那个线程呢? 还记得吗 咱们刚才建立了一个全局队列, 那么有没有切换队列呢

好像没有, 对吧, 不要紧咱们看看这个currentScheduler的setter方法

+ (RACScheduler *)currentScheduler {
    RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
    if (scheduler != nil) return scheduler;
    if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;

    return nil;
}

看到没, 它首先建立了一个RACScheduler实例, 用的NSThread的threadDIctonary去找RACScheduleCurrentScheduleKey

若是找到了就在返回这个队列, 不然若是是主线程就返回主线程队列,

咱们当前并无切换队里, 这里应该是给其余状况下使用的

好了, 咱们再看看执行的是什么block

RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];

咱们注意第一句话, 这里就执行了didSubscribe并把返回值赋给了一个RACDisposable

记得didSubscribe里面有什么吗? 对就是打印建立信号的那个block

到这里咱们就看到为何前面建立信号的时候没有调用那里的block, 原来是订阅的这个地方调用的.

因此建立信号的block要订阅的时候才会去执行

 

不过好像到这里都没有去执行咱们订阅的block, 只是把信号跟订阅捆绑到了一块儿.

那么要怎么执行订阅的block呢? 不知道你们注意到没, 咱们订阅用的是subscribeNext, 看字面意思是订阅而后作去执行

看字面理解咱们如今只是完成了订阅的操做, 但没有触发next

要怎么触发next呢?

咱们能够想一想这个触发要何时作呢? 目前只有建立和订阅 那么确定在建立的时候触发对不对

咱们看看建立信号的block里面的一个id格式的参数subscriber, 不过它有实现一个协议RACSubscriber

咱们看看里面有什么

@protocol RACSubscriber <NSObject>
@required

/// Sends the next value to subscribers.
///
/// value - The value to send. This can be `nil`.
- (void)sendNext:(nullable id)value;

/// Sends the error to subscribers.
///
/// error - The error to send. This can be `nil`.
///
/// This terminates the subscription, and invalidates the subscriber (such that
/// it cannot subscribe to anything else in the future).
- (void)sendError:(nullable NSError *)error;

/// Sends completed to subscribers.
///
/// This terminates the subscription, and invalidates the subscriber (such that
/// it cannot subscribe to anything else in the future).
- (void)sendCompleted;

/// Sends the subscriber a disposable that represents one of its subscriptions.
///
/// A subscriber may receive multiple disposables if it gets subscribed to
/// multiple signals; however, any error or completed events must terminate _all_
/// subscriptions.
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

很清楚对不对, 原来在这里,

那咱们试试看发送一个sendNext

    // 建立一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"建立一个信号");
        
        // 发送信号
        [subscriber sendNext:@"发送一个信号"];
        
        return nil;
    }];
    
    // 订阅一个信号
    [signal subscribeNext:^(id  _Nullable x) {
        
        NSLog(@"订阅一个信号");
    }];

运行看看:

2017-07-22 17:35:38.937 RAC[66240:12626323] 建立一个信号
2017-07-22 17:35:38.937 RAC[66240:12626323] 订阅一个信号

订阅的block执行了对不对, 那么还有个问题咱们发送信号到哪里去了呢

咱们把x打印看看

    // 建立一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"建立一个信号");
        
        // 发送信号
        [subscriber sendNext:@"发送一个信号"];
        
        return nil;
    }];
    
    // 订阅一个信号
    [signal subscribeNext:^(id  _Nullable x) {
        
        NSLog(@"订阅一个信号");
        NSLog(@"订阅到的: %@", x);
    }];

咱们把订阅信号block里面的参数打印看看

2017-07-22 17:37:09.294 RAC[66282:12633516] 建立一个信号
2017-07-22 17:37:09.295 RAC[66282:12633516] 订阅一个信号
2017-07-22 17:37:09.295 RAC[66282:12633516] 订阅到的: 发送一个信号

打印出来了, 原来是这样传递的 

sendNext就把内容传递到了Next的block中了

 

好了, 先写到这里 但愿有帮助你们理解建立信号 订阅信号的到底有在作什么.

 

下次咱们再来看具体的使用方法

相关文章
相关标签/搜索