最近無(wú)意間看到一個(gè)視頻講的ReactiveObjC, 覺(jué)得挺好用的 但聽(tīng)完后只是了解個(gè)大概.
在網(wǎng)上找了些文章, 有的寫(xiě)的比較易懂但看完還是沒(méi)覺(jué)得自己能比較好的使用RAC, 有的甚至讓我看不下去
這兩天剛好公司項(xiàng)目交付閑下來(lái), 想自己去啃下官方文檔
ReactiveCocoa是一個(gè)基于函數(shù)響應(yīng)式編程的OC框架.
那么什么是函數(shù)式響應(yīng)式編程呢?概念我就不講了 因?yàn)槲抑v的也不一定準(zhǔn)確, 大家可以去baidu看看大神們的解釋
下面我大概演示下響應(yīng)式編程的樣子
Masonry是比較常見(jiàn)的一個(gè)響應(yīng)式框架, 它的的用法舉例如下:
make.centerY.equalTo(self.view).offset(100);
大家注意它的用法, 點(diǎn)號(hào)調(diào)用一個(gè)事件或?qū)傩院罂梢越又c(diǎn)號(hào)調(diào)用, 這里一個(gè)比較明顯的函數(shù)響應(yīng)式編程的好處就是我們可以把一些要使用的連貫的或者有先后順序的調(diào)用方法和事件連在一起, 邏輯清晰明了的完成代碼.
那么要如何實(shí)現(xiàn)這樣的調(diào)用方式呢?
centerY.equalTo(self.view)這個(gè)能執(zhí)行的話equalTo就必須是一個(gè)返回對(duì)象的block
下面試試自己來(lái)實(shí)現(xiàn)這個(gè),
建一個(gè)Person對(duì)象, 加上跑步, 走路的方法
Class: Person; Method: run; walk;
我們拆分成幾個(gè)步驟來(lái)做, 首先實(shí)現(xiàn)
[[person run] walk];先跑, 跑累了再走
要實(shí)現(xiàn)這樣的調(diào)用的話, run就必須返回person, 為了還能繼續(xù)接著這樣調(diào)用walk也要返回person
好了, 思路就很清晰了, 我們上代碼
#import <Foundation/Foundation.h>@interface Person : NSObject- (Person *)run;- (Person *)walk;@end
#import "Person.h"@implementation Person- (Person *)run { NSLog(@"跑步"); // 延時(shí)2s [NSThread sleepForTimeInterval:2]; return self; }- (Person *)walk { NSLog(@"走路"); // 延時(shí)2s [NSThread sleepForTimeInterval:2]; return self; }@end
大家可以看到, 我們r(jià)un 跟 walk方法都會(huì)返回對(duì)象本身, 為了延時(shí)我加了個(gè)延遲2s
我們調(diào)用試試
// 創(chuàng)建對(duì)象 Person *person = [[Person alloc] init]; // 嘗試調(diào)用 [[person run] walk];
結(jié)果如下:
2017-07-21 21:59:30.962 RAC[63284:11390973] 跑步2017-07-21 21:59:33.036 RAC[63284:11390973] 走路
跟預(yù)期一致, 我們?cè)賮?lái)實(shí)現(xiàn)person.run().walk();
剛才說(shuō)了要返回一個(gè)返回值是對(duì)象的block, 我們來(lái)實(shí)現(xiàn)下 代碼如下:
- (Person * (^)())runBlock { Person * (^block)() = ^() { NSLog(@"run"); // 延時(shí)2s [NSThread sleepForTimeInterval:2]; return self; }; return block; }- (Person * (^)())walkBlock { Person * (^block)() = ^() { NSLog(@"walk"); // 延時(shí)2s [NSThread sleepForTimeInterval:2]; return self; }; return block; }
如果對(duì)block使用不熟的同學(xué)可以花點(diǎn)時(shí)間琢磨下block的結(jié)構(gòu)
我們調(diào)用試試看
// 調(diào)用block person.runBlock().walkBlock();
結(jié)果:
2017-07-22 13:58:01.306 RAC[64288:11757631] run2017-07-22 13:58:03.381 RAC[64288:11757631] walk
好了, 這樣我們就自己實(shí)現(xiàn)了一個(gè)基于函數(shù)響應(yīng)式的小Demo
常規(guī)情況下, 我們寫(xiě)代碼是一般是定義很多個(gè)變量和方法, 在不同的狀態(tài)和業(yè)務(wù)流程下去改變變量的值或者調(diào)用對(duì)應(yīng)的方法.
而RAC采用信號(hào)機(jī)制來(lái)獲取當(dāng)前的, 同時(shí)也能直接處理將來(lái)要如何修改這些值, 通過(guò)利用鏈?zhǔn)巾憫?yīng)編程來(lái)書(shū)寫(xiě)結(jié)構(gòu)邏輯清晰的代碼, 不用我們?cè)诓煌牡胤饺ソo我們屬性值做處理,
比如我們要給一個(gè)UITextField做監(jiān)聽(tīng), 當(dāng)值改變的時(shí)候做一些處理例如打印當(dāng)前輸入的值, 常規(guī)用法下我們要讓當(dāng)前控制器或者類(lèi)遵循textField的代理, 然后把textField的代理指給當(dāng)前類(lèi), 實(shí)現(xiàn)代理方法, 代碼大概會(huì)是這樣:
@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來(lái)實(shí)現(xiàn), 當(dāng)代碼比較少的時(shí)候這樣看起來(lái)還比較清晰, 如果當(dāng)時(shí)一個(gè)完整的項(xiàng)目呢, 那么多方法要寫(xiě)我們要看看某一個(gè)textField事件估計(jì)要花一些時(shí)間在代碼里面去找這個(gè)方法, 代碼就不是很直觀了.
那么RAC怎么幫助我們解決這個(gè)問(wèn)題呢, 上面有說(shuō)過(guò)RAC是通過(guò)信號(hào)來(lái)管理的, 那么什么是信號(hào)呢?
RACSignal就是這個(gè)類(lèi), 我們?cè)囋囎约簞?chuàng)建一個(gè)信號(hào) 首先我們先用Pod導(dǎo)入ReactiveObjC庫(kù)
pod 'ReactiveObjC', '~>3.0.0'
導(dǎo)入頭文件
#import <ReactiveObjC.h>
我們創(chuàng)建一個(gè)信號(hào):
// 創(chuàng)建一個(gè)信號(hào) RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(<RACSubscriber>
直接運(yùn)行看看, 好像什么都沒(méi)有發(fā)生, 怎么回事呢? 我們點(diǎn)擊創(chuàng)建新的方法看看他做了什么
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { return [RACDynamicSignal createSignal:didSubscribe]; }
他給我們返回了一個(gè)RACDynamicSignal, 這個(gè)是什么呢? 我們點(diǎn)他看看
@interface RACDynamicSignal : RACSignal+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
原來(lái)他是RACSignal的一個(gè)子類(lèi), 它也重寫(xiě)了createSignal方法, 我們現(xiàn)在實(shí)際是調(diào)用了他的創(chuàng)建信號(hào)的方法. 那我們看看它這個(gè)方法都做了什么
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { RACDynamicSignal *signal = [[self alloc] init]; signal->_didSubscribe = [didSubscribe copy]; return [signal setNameWithFormat:@"+createSignal:"]; }
它創(chuàng)建了一個(gè)RACDynamicSignal實(shí)例, 然后把didSubscribe復(fù)制了一份復(fù)制給創(chuàng)建的實(shí)例, 然后重命名后就直接返回給我們了.
然后就結(jié)束了, 難怪我們什么效果都沒(méi)有看到
RAC里面有一個(gè)很重要的理念: 創(chuàng)建信號(hào)必須訂閱, 訂閱了信號(hào)才會(huì)被執(zhí)行.
沒(méi)有訂閱的信號(hào)是冷信號(hào) 不會(huì)產(chǎn)生任何效果, 訂閱信號(hào)就從冷信號(hào)變成熱信號(hào), 就可以執(zhí)行各種操作.
我們看看如何訂閱:
// 創(chuàng)建一個(gè)信號(hào) RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"創(chuàng)建一個(gè)信號(hào)"); return nil; }]; // 訂閱一個(gè)信號(hào) [signal subscribeNext:^(id _Nullable x) { NSLog(@"訂閱一個(gè)信號(hào)"); }];
我們運(yùn)行看看
2017-07-22 15:05:58.760 RAC[65085:12004278] 創(chuàng)建一個(gè)信號(hào)
創(chuàng)建信號(hào)的block執(zhí)行了, 但是訂閱的信號(hào)沒(méi)有執(zhí)行, 我們看看點(diǎn)開(kāi)subscribeNext看看為什么
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock { NSCParameterAssert(nextBlock != NULL); RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL]; return [self subscribe:o]; }
它首先判斷我們的block不會(huì)空, 然后創(chuàng)建了一個(gè)RACSubscriber訂閱者, 并把我們的block給它了
再點(diǎn)subscriber的創(chuàng)建方法看看它做了什么
+ (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; }
它只是創(chuàng)建了一個(gè)subscriber實(shí)例, 然后把我們的block拷貝給它 還是什么都沒(méi)有做
我們?cè)倏纯?/p>
[self subscribe:o];
做了什么
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { NSCAssert(NO, @"This method must be overridden by subclasses"); return nil; }
它做了非空判斷, 然后說(shuō)這個(gè)方法必須被子類(lèi)重寫(xiě), 這里好像也啥都沒(méi)干啊 怎么創(chuàng)建信號(hào)的block就執(zhí)行了呢?
大家想想, 我們剛才創(chuàng)建信號(hào)的時(shí)候, 是不是就是調(diào)用的是RACSignal的子類(lèi)DynamicSignal, 所以這里實(shí)際上運(yùn)行的也是這個(gè)DynamicSignal的subscribe方法, 我們?nèi)タ纯?/p>
- (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; }
首先它也是先判斷是否為空, 然后創(chuàng)建了一個(gè)RACCompoundDisposable實(shí)例
接著有給我們的subscriber重新賦值, 我們看看這個(gè)RACPassthroughSubscriber
// A private subscriber that passes through all events to another subscriber// while not disposed.@interface RACPassthroughSubscriber : NSObject <RACSubscriber>
它是把事件從一個(gè)subscriber傳遞給另外一個(gè)subscriber, 所以這里就是它把我們?cè)械膕ubscriber + 之前創(chuàng)建的signal + disposable加起來(lái)組成一個(gè)新的subscriber重新賦值給我們的subscriber, 相當(dāng)于把我們創(chuàng)建的信號(hào)跟訂閱綁定到一起了
接著如果didsubscribe不為空的話, 及繼續(xù)執(zhí)行否則直接返回disposable
我們的didsubscriber大家還記得是什么嗎? 打印創(chuàng)建信號(hào)那段對(duì)吧
然后我們看到它創(chuàng)建了一個(gè)RACDisposable實(shí)例, 但是它用的是一個(gè)RACScheduler來(lái)創(chuàng)建的
我們看看這個(gè)RACScheduler是個(gè)啥
/// Schedulers are used to control when and where work is performed.@interface RACScheduler : NSObject
哦 它是一個(gè)類(lèi)似Timer或者dispatch_after的東西, 控制事件在什么時(shí)候觸發(fā)
我們?cè)倏纯催@個(gè)subscriptionScheduler
+ (RACScheduler *)subscriptionScheduler { static dispatch_once_t onceToken; static RACScheduler *subscriptionScheduler; dispatch_once(&onceToken, ^{ subscriptionScheduler = [[RACSubscriptionScheduler alloc] init]; }); return subscriptionScheduler; }
它創(chuàng)建了一個(gè)RACScheduler單例, 不過(guò)是用RACSubscriptionScheduler來(lái)初始化的, 我們?cè)倏纯此?/p>
@interface RACSubscriptionScheduler : RACScheduler
是一個(gè)RACSchedule的子類(lèi), 它重寫(xiě)的初始化和schedule , after...等等方法, 先記下一會(huì)看看是否用到了這些重寫(xiě)的方法
這里我們先看看這個(gè)子類(lèi)重寫(xiě)的初始化方法
- (instancetype)init { self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.subscriptionScheduler"]; _backgroundScheduler = [RACScheduler scheduler]; return self; }
重命名, 然后給持有的一個(gè)RACScheduler對(duì)象backgroundScheduler賦值, 我們看看RACScheduler的scheduler做了什么
+ (RACScheduler *)scheduler { return [self schedulerWithPriority:RACSchedulerPriorityDefault]; }
繼續(xù)點(diǎn)
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority { return [self schedulerWithPriority:priority name:@"org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler"]; }
還是看不出來(lái), 繼續(xù)點(diǎn)
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name { return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)]; }
返回了一個(gè)RACTargetQueueScheduler實(shí)例, targetQueue是一個(gè)dispatch_get_global_queue全局隊(duì)列
/// 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;
一個(gè)類(lèi)似隊(duì)列的東西, 看看它的初始化方法
- (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]; }
前面很清晰, 創(chuàng)建了一個(gè)隊(duì)列
看看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; }
http://www.cnblogs.com/zhouxihi/p/7222063.html