最近無心間看到一個視頻講的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中了
好了, 先寫到這裏 但願有幫助你們理解建立信號 訂閱信號的到底有在作什麼.
下次咱們再來看具體的使用方法