ReactiveCocoa是github開(kāi)源的一個(gè)
函數(shù)式響應(yīng)式編程(FRP)框架
-
函數(shù)式吶應(yīng)式編程(FRP)
函數(shù)式響應(yīng)式,這里面包含了兩個(gè)編程風(fēng)格.即函數(shù)式(Functional Programming)和響應(yīng)式(Reactive Programming),這里先介紹函數(shù)式,在說(shuō)函數(shù)式之前先介紹鏈?zhǔn)骄幊?
-
鏈?zhǔn)骄幊?/p>
是將多個(gè)操作(多行代碼)通過(guò)點(diǎn)號(hào)(.)鏈接在一起成為一句代碼,使代碼可讀性好。a(1).b(2).c(3),常用框架Masonry.我們的主題是從計(jì)算器到ReactiveCocoa,就拿計(jì)算器為例了。
@interface CalculatorMaker : NSObject @property (nonatomic, assign) int result; //加法 - (CalculatorMaker *(^)(int value))add; //減法 - (CalculatorMaker *(^)(int value))sub; //歸0 - (CalculatorMaker *(^)(void))clean; - (void)makeCalculator:(void(^)(CalculatorMaker *mark))markBlock; @end @implementation CalculatorMaker - (CalculatorMaker *(^)(int value))add { return ^CalculatorMaker *(int a) { self.result += a; return self; }; } - (CalculatorMaker *(^)(int value))sub { return ^CalculatorMaker *(int a) { self.result -= a; return self; }; } - (CalculatorMaker *(^)(void))clean { return ^CalculatorMaker *(void) { self.result = 0; return self; }; } - (void)makeCalculator:(void(^)(CalculatorMaker *mark))markBlock { markBlock(self); } @end //sample CalculatorMaker *calculator = [[CalculatorMaker alloc] init]; [calculator makeCalculator:^(CalculatorMaker *mark) { mark.add(2).sub(1).clean().add(3); }]; NSLog(@"~~~~ value %d",calculator.result); /*output ~~~~ value 3 */這里要問(wèn)一下為什么能通過(guò).操作符連接操作,回想一下.操作符在OC中實(shí)際是防問(wèn)屬性的get和set方法的一種簡(jiǎn)寫.所以我們來(lái)拆開(kāi)
CalculatorMaker *calculator = [[CalculatorMaker alloc] init]; [calculator makeCalculator:^(CalculatorMaker *mark) { CalculatorMaker *(^tmpAddBlock)(int) = [mark add]; CalculatorMaker *tmpAddReturn = tmpAddBlock(2); CalculatorMaker *(^tmpSubBlock)(int) = [tmpAddReturn sub]; CalculatorMaker *tmpSubReturn = tmpSubBlock(1); CalculatorMaker *(^tmpCleanBlock)(void) = [tmpSubReturn clean]; CalculatorMaker *tmpCleanReturn = tmpCleanBlock(); CalculatorMaker *(^tmpAdd2Block)(int) = [tmpCleanReturn add]; CalculatorMaker *tmpAdd2Return = tmpAdd2Block(3); }]; NSLog(@"~~~~ value %d",calculator.result); /*output ~~~~ value 3 */ //所以兩者結(jié)果是完全一樣的接下來(lái)介紹函數(shù)式編程,在介紹這個(gè)之前先介紹兩個(gè)很有意思的概念
-
side effects(副作用)
副作用是在計(jì)算結(jié)果的過(guò)程中,系統(tǒng)狀態(tài)的一種改變,或是外部世界可觀察的交互作用。比如,更改檔案系統(tǒng),發(fā)送http請(qǐng)求等,只要與function外部環(huán)境發(fā)生交互作用的都是副作用。
-
pure function(純函數(shù))
定義:相同輸入,永遠(yuǎn)會(huì)得到相同的輸出,而且沒(méi)有任何顯著副作用.
擴(kuò)展解釋:函數(shù)與外界交互只有唯二通道,參數(shù)、返回值(輸入,輸出)。同時(shí)參數(shù)應(yīng)該是只讀的,函數(shù)不應(yīng)該修改參數(shù)內(nèi)部數(shù)據(jù),以下舉幾個(gè)例子
//例一:相同輸入,永遠(yuǎn)會(huì)得到相同的輸出,沒(méi)有副作用 var min = 1; var OuterCompareNumber = function(number) { return number > min; } var InnerCompareNumber = function(number) { var min = 1; return number > min; } //OuterCompareNumber非純函數(shù).min可能會(huì)被外界改變 //InnerCompareNumber是純函數(shù)//例二:沒(méi)有任何顯著副作用 var callRequest = function(url,paramas) { return $.getJSON(url, paramas); } var delayCallRequest = function(url,paramas) { return function() { return $.getJSON(url, paramas); } } /* callRequest(url, paramas); delayCallRequest(url, paramas)(); callRequest直接返回了一個(gè)http請(qǐng)求,那么這個(gè)是與外界交互, 返回的結(jié)果有太大的不確定性,所以是impure的函數(shù); 而delayCallRequest采用了延遲執(zhí)行的方式,返回了一個(gè)函數(shù), 只有調(diào)用這個(gè)函數(shù)的時(shí)候才會(huì)發(fā)送請(qǐng)求:delayCallRequest(url, params)(), 但就delayCallRequest而言,傳相同的參數(shù),得到的結(jié)果是一樣的,是相同參數(shù)的函數(shù), 所以這個(gè)是一個(gè)pure的函數(shù)。 */先簡(jiǎn)單了解一下這兩個(gè)概念,下面會(huì)有用到。純函數(shù)有如下好處.
1.無(wú)狀態(tài),Stateless。線程安全。不需要線程同步。
2.Pure Function相互調(diào)用組裝起來(lái)的函數(shù),還是Pure Function。
3.應(yīng)用程序或者運(yùn)行環(huán)境(Runtime)可以對(duì)Pure Function的運(yùn)算結(jié)果進(jìn)行緩存,運(yùn)算加快速度。
我們繼續(xù)了解函數(shù)式編
-
-
-
函數(shù)式編程
函數(shù)是第一等公民,能像其它普通變量一樣去,創(chuàng)建,修改,傳遞,我們看可以把block看成函數(shù)
@interface CalculatorFP : NSObject @property (nonatomic, assign) int result; - (CalculatorFP *)add:(int(^)(void))block; - (CalculatorFP *)sub:(int(^)(void))block; - (CalculatorFP *)clean; @end @implementation CalculatorFP - (CalculatorFP *)add:(int(^)(void))block { int value = block(); self.result += value; return self; } - (CalculatorFP *)sub:(int(^)(void))block { int value = block(); self.result -= value; return self; } - (CalculatorFP *)clean { self.result = 0; return self; } @end //sample CalculatorFP *fp = [[CalculatorFP alloc] init]; [[[[fp add:^int{ return 2; }] sub:^int{ return 1; }] clean] add:^int{ return 3; }]; NSLog(@"~~~~ fp value %d",fp.result); /*output ~~~~ fp value 3 */上面的計(jì)算器的例子,已經(jīng)完全夠用。然后我們擴(kuò)展思考一下。假如,我們想表示一個(gè)計(jì)算表達(dá)式如 int c = a + b;并且在a或b值改變的時(shí)候.c能同步改變。怎么做?首先我們要實(shí)現(xiàn)a,b改變的時(shí)候得到通知,所以我們介紹另一個(gè)編程思想,響應(yīng)式編程
-
響應(yīng)式編程
直接上代碼
@interface CalculatorReactive : NSObject @property (nonatomic, assign) int value; - (void)addObserverValueUpdate:(void(^)(int a))block; //這個(gè)方法返回一個(gè)block,調(diào)用該block,就能移除block的監(jiān)聽(tīng) - (void(^)(void))addWithRemoveControllerObserverValueUpdate:(void(^)(int))block; @end @interface CalculatorReactive () @property (nonatomic, strong) NSMutableArray<void (^)(int)> *recordObserverUpdateBlockArray; @end @implementation CalculatorReactive - (id)init { if (self = [super init]) { self.recordObserverUpdateBlockArray = [NSMutableArray array]; } return self; } - (void)addObserverValueUpdate:(void(^)(int))block { [_recordObserverUpdateBlockArray addObject:block]; } - (void(^)(void))addWithRemoveControllerObserverValueUpdate:(void(^)(int))block { void(^removeBlock)(void) = ^{ [_recordObserverUpdateBlockArray removeObject:block]; }; [_recordObserverUpdateBlockArray addObject:block]; return removeBlock; } - (void)setValue:(int)value { if (_value != value) { _value = value; [_recordObserverUpdateBlockArray enumerateObjectsUsingBlock:^(void (^ _Nonnull obj)(int), NSUInteger idx, BOOL * _Nonnull stop) { obj(value); }]; } } @end //sample CalculatorReactive *a = [[CalculatorReactive alloc] init]; [a addObserverValueUpdate:^(int v) { NSLog(@"a new value is %d",v); }]; void(^removeBlock)(void) = [a addWithRemoveControllerObserverValueUpdate:^(int v) { NSLog(@"a with remove new value %d",v); }]; a.value = 10; a.value = 5; removeBlock(); a.value = 11; /*output a new value is 10 a with remove new value 10 a new value is 5 a with remove new value 5 a new value is 11 */OK,為了達(dá)成int c = a + b;完成了第一步,我們能監(jiān)聽(tīng)a,b變化了。為了進(jìn)一步完成目前我們把響應(yīng)式和函數(shù)式結(jié)合一起。
-
函數(shù)式響應(yīng)式編程
@interface CalculatorReactiveFP : CalculatorReactive - (CalculatorReactiveFP *)addByOther:(CalculatorReactiveFP *)other; - (CalculatorReactiveFP *)subByOther:(CalculatorReactiveFP *)other; @end @implementation CalculatorReactiveFP - (void)commondDoBlock:(void(^)(void))block other:(CalculatorReactiveFP *)other { void(^tmpObserverValueUpdateBlock)(int) = ^(int a) { block(); }; [self.recordObserverUpdateBlockArray addObject:tmpObserverValueUpdateBlock]; [other.recordObserverUpdateBlockArray addObject:tmpObserverValueUpdateBlock]; } - (CalculatorReactiveFP *)addByOther:(CalculatorReactiveFP *)other { CalculatorReactiveFP *result = [[CalculatorReactiveFP alloc] init]; [self commondDoBlock:^void{ int newValue = self.value + other.value; result.value = newValue; } other:other]; return result; } - (CalculatorReactiveFP *)subByOther:(CalculatorReactiveFP *)other { CalculatorReactiveFP *result = [[CalculatorReactiveFP alloc] init]; [self commondDoBlock:^void{ int newValue = self.value - other.value; result.value = newValue; } other:other]; return result; } @end //sample CalculatorReactiveFP *a = [[CalculatorReactiveFP alloc] init]; [a addObserverValueUpdate:^(int v) { NSLog(@"a new value is %d",v); }]; CalculatorReactiveFP *b = [[CalculatorReactiveFP alloc] init]; [b addObserverValueUpdate:^(int v) { NSLog(@"b new value is %d",v); }]; CalculatorReactiveFP *c = [a addByOther:b]; [c addObserverValueUpdate:^(int v) { NSLog(@"c new value is %d",v); }]; CalculatorReactiveFP *e = [[c addByOther:a] subByOther:b]; [e addObserverValueUpdate:^(int v) { NSLog(@"e new value is %d",v); }]; a.value = 1; b.value = 3; a.value = 5; c.value = 11; b.value = 2; /*output a new value is 1 c new value is 1 e new value is 2 b new value is 3 c new value is 4 a new value is 5 c new value is 8 e new value is 10 c new value is 11 e new value is 13 b new value is 2 c new value is 7 e new value is 10 */我們基本實(shí)現(xiàn)了int c = b + a;這種運(yùn)用算,用到了函數(shù)式響應(yīng)式編程.ReactiveCocoa也是函數(shù)式響應(yīng)式編程,當(dāng)然我們跟他差很多,但是核心思想是一樣,我們關(guān)注結(jié)果,不關(guān)注具體過(guò)程.這非常重要,請(qǐng)一定記住,響應(yīng)式編程的重點(diǎn)是關(guān)注事務(wù)的關(guān)系.不關(guān)注過(guò)程是怎么樣進(jìn)行的。
為了更接近ReactiveCocoa,我開(kāi)始進(jìn)一步優(yōu)化,第一步.我們要的不是只支持計(jì)算器,要支持任何數(shù)據(jù)類型,OC中id類型就是泛型.同時(shí)作為管道我們不需要去記錄傳遞過(guò)來(lái)值。接下來(lái),我們離開(kāi)計(jì)算器,我們以Cocoa作為類名前綴.
//為了方便閱讀,把一些常的block加上別名 typedef void(^CCSubscribeBlock)(id value); typedef void(^CCBlankBlock)(void); @interface CocoaReactive : NSObject //把方法名,由set/observer->send/subscribe - (void)sendValue:(id)value; - (CCBlankBlock)subscribeValue:(CCSubscribeBlock)block; @end @interface CocoaReactive () @property (nonatomic, strong) NSMutableArray<CCSubscribeBlock> *recordObserverUpdateBlockArray; @end @implementation CocoaReactive - (id)init { if (self = [super init]) { self.recordObserverUpdateBlockArray = [NSMutableArray array]; } return self; } - (void)sendValue:(id)value { [_recordObserverUpdateBlockArray enumerateObjectsUsingBlock:^(void (^ _Nonnull obj)(id), NSUInteger idx, BOOL * _Nonnull stop) { obj(value); }]; } - (CCBlankBlock)subscribeValue:(CCSubscribeBlock)block { CCBlankBlock removeBlock = ^{ [_recordObserverUpdateBlockArray removeObject:block]; }; [_recordObserverUpdateBlockArray addObject:block]; return removeBlock; } @end @interface CocoaReactiveFP : CocoaReactive - (CocoaReactiveFP *)processValue:(id(^)(id v))block; @end @implementation CocoaReactiveFP - (CocoaReactiveFP *)processValue:(id(^)(id))block { CocoaReactiveFP *result = [[CocoaReactiveFP alloc] init]; void(^tmpSubscribeBlock)(id) = ^(id a) { id newValue = block(a); [result sendValue:newValue]; }; [self subscribeValue:tmpSubscribeBlock]; return result; } @end //sample CocoaReactiveFP *a = [[CocoaReactiveFP alloc] init]; CocoaReactiveFP *b = [[a processValue:^id(id v) { int tmpV = [v intValue]; return @(tmpV + 3); }] processValue:^id(id v) { int tmpV = [v intValue]; return @(tmpV + 5); }]; [b subscribeValue:^(id v) { NSLog(@"b new value %@",v); }]; [a sendValue:@(2)];; /*output b new value 10 */上面的例子,可以清楚的看到流程:a收到一個(gè)值,b就會(huì)在這基礎(chǔ)上+3再+5,然后再返回這個(gè)值給b.我們大概實(shí)現(xiàn)了一個(gè)管道作用于另一個(gè)管道。即 b = a + (N個(gè)block);我們能否實(shí)現(xiàn) c = a + b;,a,b同時(shí)是管道呢,當(dāng)然能,我們返回值變成一個(gè)管道試試。我們搞個(gè)分類
@interface CocoaReactiveFP (optimize) - (CocoaReactiveFP *)processValue2:(CocoaReactiveFP *(^)(id v))block; @end @implementation CocoaReactiveFP (optimize) - (CocoaReactiveFP *)processValue2:(CocoaReactiveFP *(^)(id v))block { CocoaReactiveFP *result = [[CocoaReactiveFP alloc] init]; void(^tmpSubscribeBlock)(id) = ^(id a) { //這里返回了一個(gè)管道,我們訂閱這個(gè)管道,當(dāng)它有值過(guò)來(lái),我們就傳給result CocoaReactiveFP *reactive = block(a); void(^tmpSubscribeReactiveBlock)(id) = ^(id a) { [result sendValue:a]; }; [reactive subscribeValue:tmpSubscribeReactiveBlock]; }; [self subscribeValue:tmpSubscribeBlock]; return result; } @end //sample CocoaReactiveFP *a = [[CocoaReactiveFP alloc] init]; CocoaReactiveFP *b = [[CocoaReactiveFP alloc] init]; CocoaReactiveFP *c = [a processValue2:^CocoaReactiveFP *(id v) { return b; }]; [c subscribeValue:^(id v) { NSLog(@"c new value %@",v); }]; [a sendValue:@(2)]; [b sendValue:@(20)]; /*output c new value 20 */我們實(shí)現(xiàn)返回一個(gè)全新的管道.然后訂閱它,它的值傳過(guò)來(lái)后我們傳給我們要的管道。但是這段代碼怎么看都不舒服,它有以下三個(gè)問(wèn)題.
1. 不夠Lazy,管道b直接就初始化出來(lái)的,我希望,應(yīng)該是[c subscribeValue:...];時(shí)候創(chuàng)建 2. 管道a的值被無(wú)視了,直接無(wú)用了。 3. 回顧一下我上面提到的side effects/pure function.processValue2的參數(shù)block,不是prue function。它防問(wèn)了外部變量b,產(chǎn)生了副作用.怎么改呢,可以參考上面的例子,將防問(wèn)外部變量打包到一個(gè)函數(shù)中,然后將這個(gè)函數(shù)作為返回值.這里的函數(shù)即我們的block.我們動(dòng)手改吧,@class CocoaReactiveFP; //方便閱讀我們將方法processValue2 的參數(shù)block加別名 typedef CocoaReactiveFP *(^CCBindBlock)(id value); @interface CalculatorReactiveFP (Lazy) - (CocoaReactiveFP *)processValueLazy:(CCBindBlock(^)(void))block; @end @implementation CocoaReactiveFP (Lazy) - (CocoaReactiveFP *)processValueLazy:(CCBindBlock(^)(void))block { CocoaReactiveFP *result = [[CocoaReactiveFP alloc] init]; void(^tmpSubscribeBlock)(id) = ^(id a) { //block將在有subscribe的時(shí)候才調(diào)用 CCBindBlock bindBlock = block(); CocoaReactiveFP *bindReactive = bindBlock(a); void(^tmpBindReactiveSubscribeBlock)(id) = ^(id a) { [result sendValue:a]; }; [bindReactive subscribeValue:tmpBindReactiveSubscribeBlock]; }; [self subscribeValue:tmpSubscribeBlock]; return result; } @end //sample CocoaReactiveFP *aLazy = [[CocoaReactiveFP alloc] init]; CocoaReactiveFP *cLazy = [aLazy processValueLazy:^CCBindBlock{ return ^CocoaReactiveFP *(id v){ CocoaReactiveFP *bLazy = [[CocoaReactiveFP alloc]init]; //尷尬的我們發(fā)現(xiàn)bLazy,無(wú)處sendValue.在這sendValue的話, //cLazy還未subscribe bLazy. return bLazy; }; }]; [cLazy subscribeValue:^(id value) { NSLog(@"cLazy new value %@",value); }]; [aLazy sendValue:@(2)]; [aLazy sendValue:@(5)];看到上面好尷尬,我們無(wú)法對(duì)bLazy調(diào)用sendValue,想了想,我們需要給bLazy開(kāi)個(gè)block,當(dāng)有人訂閱它的時(shí)候,通知我們,我們就可以在那個(gè)block里面sendValue,馬上動(dòng)手
@interface CocoaReactiveFP (Lazy2) - (CocoaReactiveFP *)createReactiveFPLazy:(void(^)(CCSubscribeBlock o))block; @end @interface CocoaReactiveFP () // @property (nonatomic, copy) void (^createBlock)(CCSubscribeBlock o); @end @implementation CocoaReactiveFP (Lazy2) - (CocoaReactiveFP *)createReactiveFPLazy:(void(^)(CCSubscribeBlock o))block { CocoaReactiveFP *lazy = [[CocoaReactiveFP alloc] init]; lazy.createBlock = block; return lazy; } - (CCBlankBlock)subscribeValue:(CCSubscribeBlock)block { CCBlankBlock result = [super subscribeValue:block]; //當(dāng)有人subscribe的時(shí)候我們就調(diào)用_createBlock通知外界 if (_createBlock) { _createBlock(block); } return result; } @end //sample CocoaReactiveFP *aLazy = [[CocoaReactiveFP alloc] init]; CocoaReactiveFP *cLazy = [aLazy processValueLazy:^CCBindBlock{ return ^CocoaReactiveFP *(id v){ CocoaReactiveFP *bLazy = [CocoaReactiveFP createReactiveFPLazy:^(CCSubscribeBlock o) { o(@([v intValue] + 5)); }]; return bLazy; }; }]; [cLazy subscribeValue:^(id value) { NSLog(@"cLazy new value %@",value); }]; [aLazy sendValue:@(2)]; [aLazy sendValue:@(5)]; /*output cLazy new value 7 cLazy new value 10 */很好,這么寫解決了上面三個(gè)問(wèn)題,bLazy,是lazy create.processValueLazy的參數(shù)block沒(méi)有副作用,是純函數(shù).a管道的值也得到利用了.總體來(lái)說(shuō),還算不錯(cuò).我們還有最后一步
我們重新思考一下函數(shù)式響應(yīng)式編程.首先它由兩種編程風(fēng)格結(jié)合在一起,在OC單繼承語(yǔ)種上,他們是有先后的,我們上面的例子,是吶應(yīng)式在前,函數(shù)式在后.在ReactiveCocoa中,是相反的,函數(shù)式在前,響應(yīng)式在后.萬(wàn)物皆是流,我們改一下.同時(shí)有些微調(diào)。
@interface CocoaSubscriber : NSObject + (instancetype)subscriberWithValueBlock:(CCSubscribeBlock)block; - (void)sendValue:(id)value; @end @class CocoaStream; typedef CocoaStream *(^CCStreamBindBlock)(id value); @interface CocoaStream : NSObject - (__kindof CocoaStream *)bind:(CCStreamBindBlock(^)(void))block; @end @interface CocoaSignal : CocoaStream - (CocoaSignal *)createSignal:(void(^)(CocoaSubscriber *subscriber))createBlock; - (CCBlankBlock)subscribeValue:(CCSubscribeBlock)block; @end @interface CocoaSubscriber () @property (nonatomic, copy) CCSubscribeBlock valueBlock; @property (nonatomic, readonly) CCBlankBlock dispose; @end @implementation CocoaSubscriber - (instancetype)subscriberWithValueBlock:(CCSubscribeBlock)block { CocoaSubscriber *result = [[CocoaSubscriber alloc] init]; result.valueBlock = block; return result; } - (id)init { if (self = [super init]) { __weak CocoaSubscriber *wself = self; _dispose = ^{ __weak CCSubscribeBlock wblock = wself.valueBlock; if (wblock) { wblock = NULL; } }; } return self; } - (void)sendValue:(id)value { if (_valueBlock) { _valueBlock(value); } } @end @implementation CocoaStream - (__kindof CocoaStream *)bind:(CCStreamBindBlock(^)(void))block { NSString *reason = [NSString stringWithFormat:@"%@ must be overridden by subclasses", NSStringFromSelector(_cmd)]; @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil]; } @end @interface CocoaSignal () @property (nonatomic, copy) void(^createBlock)(CocoaSubscriber *o); @end @implementation CocoaSignal - (CocoaSignal *)createSignal:(void(^)(CocoaSubscriber *subscriber))createBlock; { CocoaSignal *signal = [[CocoaSignal alloc] init]; signal.createBlock = createBlock; return signal; } - (CCBlankBlock)subscribeValue:(CCSubscribeBlock)block { CocoaSubscriber *subscriber = [CocoaSubscriber subscriberWithValueBlock:block]; if (_createBlock) { _createBlock(subscriber); } return subscriber.dispose; } - (CocoaSignal *)bind:(CCStreamBindBlock(^)(void))block { return [CocoaSignal createSignal:^(CocoaSubscriber *subscriber) { CCStreamBindBlock bindBlock = block(); void(^tmpSubscribeBlock)(id) = ^(id a) { CocoaSignal *bindReactive = (CocoaSignal *)bindBlock(a); void(^tmpBindReactiveSubscribeBlock)(id) = ^(id a) { [subscriber sendValue:a]; }; [bindReactive subscribeValue:tmpBindReactiveSubscribeBlock]; }; [self subscribeValue:tmpSubscribeBlock]; }]; } @end //sample CocoaSignal *aSignal = [CocoaSignal createSignal:^(CocoaSubscriber *subscriber) { [subscriber sendValue:@(2)]; [subscriber sendValue:@(5)]; }]; CocoaSignal *cSignal = [aSignal bind:^CCStreamBindBlock{ return ^CocoaSignal *(id v){ CocoaSignal *bSignal = [CocoaSignal createSignal:^(CocoaSubscriber *subscriber) { int newValue = [v intValue] + 5; [subscriber sendValue:@(newValue)]; }]; return bSignal; }; }]; [cSignal subscribeValue:^(id value) { NSLog(@"cSignal new value %@",value); }]; /*output cSignal new value 7 cSignal new value 10 */沒(méi)錯(cuò),這是微調(diào),我感覺(jué)有點(diǎn)ReactiveCocoa的模樣了,你們覺(jué)得呢?
-
總結(jié)
如果通遍下來(lái)要做個(gè)總結(jié)的話,我覺(jué)得的最最最重要的是,思想的轉(zhuǎn)變.
RAC是構(gòu)建事務(wù)關(guān)系的工具,不要用命令式那種關(guān)注過(guò)程的思想去思考它。 demo
github-
題外話
這個(gè)是ReactiveCocoa分享的第一節(jié),很多細(xì)節(jié)解釋在分享上講。