最近每周末都會(huì)看葉孤城的直播卓囚,對(duì)于我們iOS開(kāi)發(fā)者來(lái)說(shuō),確實(shí)是一個(gè)福利诫肠,很感謝他們的分享精神憨募,收獲到的一些東西特此記錄下紧索。
12月19號(hào) ReactiveCocoa
昨天聽(tīng)了DeveloperLx的視頻之后,對(duì)ReactiveCocoa有了個(gè)初步的認(rèn)識(shí)下菜谣,暫時(shí)可能不會(huì)用到珠漂,但是了解還是必須的。ReactiveCocoa(簡(jiǎn)稱為RAC),是由Github開(kāi)源的一個(gè)應(yīng)用于iOS和OS開(kāi)發(fā)的新框架,兼具
函數(shù)式編程
和響應(yīng)式編程
的特性,可以很好的用于消息傳遞尾膊、回調(diào)機(jī)制復(fù)雜等問(wèn)題媳危,使之清晰化,條理化冈敛。
ReactiveCocoa結(jié)合了一些編程模式:
- 函數(shù)式編程:利用高階函數(shù)待笑,即將函數(shù)作為其它函數(shù)的參數(shù)。
- 響應(yīng)式編程:關(guān)注于數(shù)據(jù)流及變化的傳播抓谴。
基于以上兩點(diǎn)暮蹂,ReactiveCocoa被當(dāng)成是函數(shù)響應(yīng)編程(Functional Reactive Programming, FRP)框架。
一癌压、導(dǎo)入ReactiveCocoa 框架
我們可以直接進(jìn)入到ReactiveCocoa的github了解下仰泻,通常我們用CocoaPods就OK啦
pod 'ReactiveCocoa'
很多情況下,直接導(dǎo)入就可以了滩届,但是這里會(huì)報(bào)這個(gè)錯(cuò)
需要在Podfile加上use_frameworks!
集侯,重新pod install 才能導(dǎo)入成功
use_frameworks!
pod 'ReactiveCocoa'
但是我使用Xcode7.2的時(shí)候,還是出現(xiàn)下面這個(gè)問(wèn)題
Box.swift: error: 'Printable' has been renamed to 'CustomStringConvertible'
Box.swift: error: 'toString' has been renamed to 'String'
Box/MutableBox.swift: error: 'Printable' has been renamed to 'CustomStringConvertible'
MutableBox.swift: error: 'toString' has been renamed to 'String'
大致原因是 這個(gè)默認(rèn)的分支中 swift 不支持swift2.0版的帜消,然后我就視圖轉(zhuǎn)換成~> 4.0.4-alpha-1
就OK了
use_frameworks!
pod 'ReactiveCocoa','~> 4.0.4-alpha-1'
If you would prefer to use CocoaPods, there are some unofficial pod specs) that have been generously contributed by third parties
二棠枉、基本使用
#import <ReactiveCocoa/ReactiveCocoa.h> // 導(dǎo)入頭文件
2-1、監(jiān)聽(tīng)文本框使用
- (void)learnRACWithTextFiled
{
// // 直接監(jiān)聽(tīng) textFiled的改變
// [[self.testTextField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x){
//
// NSLog(@"%@", x);
//
//
// }];
// 或者
[self.testTextField.rac_textSignal subscribeNext:^(NSString * textString) {
NSLog(@"%@", textString);
}];
}
// 打印出其textFiled中的文本信息來(lái)
2-3泡挺、 監(jiān)聽(tīng)Button事件
- (void)learnRACWithButton
{
[[self.testButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按鈕被點(diǎn)擊了");
}];
}
2-3辈讶、手勢(shì)
- (void)learnRACWithGesture
{
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
[self.view addGestureRecognizer:tap];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
// 點(diǎn)擊可以
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
}];
}
2-4杀赢、通知
- (void)learnRACWithNSNotificationCenter
{
// 通知可以不移除
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:UIKeyboardWillShowNotification object:nil]
subscribeNext:^(NSNotification * notification) {
NSLog(@"show");
}];
}
2-5哈雏、定時(shí)器
- (void)learnRACWithNSTimer
{
NSLog(@"begin");
// 1. 延遲某個(gè)時(shí)間后再做某件事
[[RACScheduler mainThreadScheduler]afterDelay:2.0f schedule:^{
NSLog(@"2秒之后發(fā)生的事情");
}];
// 2. 每個(gè)一定長(zhǎng)度時(shí)間做一件事
[[RACSignal interval:4 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
NSLog(@"每隔幾秒發(fā)生的事情");
}];
/*
2015-12-21 13:22:23.209 ReactiveCocoaLearn[78775:4675706] begin
2015-12-21 13:22:25.409 ReactiveCocoaLearn[78775:4675706] 2秒之后發(fā)生的事情
2015-12-21 13:22:27.213 ReactiveCocoaLearn[78775:4675706] 每隔幾秒發(fā)生的事情
2015-12-21 13:22:31.211 ReactiveCocoaLearn[78775:4675706] 每隔幾秒發(fā)生的事情
*/
}
2-6寡壮、代理
但是有局限弱判,只能取代沒(méi)有返回值的代理方法
- (void)learnRACWithProtocol
{
UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"RAC中Protocol"
message:@"UIAlertView"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK", nil];
[alertView show];
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
//可以多嘗試下RACTuple里的屬性
NSLog(@"tuple.second == %@",tuple.second);
if([tuple.second isEqualToNumber:@0])
{
NSLog(@"cancel");
}
if([tuple.second isEqualToNumber:@1])
{
NSLog(@"ok");
}
}];
// 更簡(jiǎn)單的方式:
// [[alertView rac_buttonClickedSignal]subscribeNext:^(id x) {
// //可以多嘗試下RACTuple里的屬性
// NSLog(@"%@",x);
// if([x isEqualToNumber:@0])
// {
// NSLog(@"Cancel");
// }
// if([x isEqualToNumber:@1])
// {
// NSLog(@"Ok");
// }
//
// }];
}
2-7、KVO
[RACObserve(self.testScrollerView, contentOffset) subscribeNext:^(id x) {
NSLog(@"Offset=%@",x);
}];
/*
2015-12-21 15:06:23.689 ReactiveCocoaLearn[81607:4756461] Offset=NSPoint: {0, 0}
2015-12-21 15:06:23.689 ReactiveCocoaLearn[81607:4756461] Offset=NSPoint: {0, 0}
2015-12-21 15:06:23.711 ReactiveCocoaLearn[81607:4756461] Offset=NSPoint: {0, -1}
2015-12-21 15:06:23.790 ReactiveCocoaLearn[81607:4756461] Offset=NSPoint: {0, -1.5}
2015-12-21 15:06:23.870 ReactiveCocoaLearn[81607:4756461] Offset=NSPoint: {0, -2}
*/
或是
[[self.greenView rac_valuesAndChangesForKeyPath:@"center"
options:NSKeyValueObservingOptionNew observer:nil]
subscribeNext:^(id x) {
NSLog(@"center===%@",x);
}];
/*
center===<RACTuple: 0x7fc7205138c0> (
"NSPoint: {187.5, 333.5}",
{
kind = 1;
new = "NSPoint: {187.5, 333.5}";
}
)
*/
以上是一些RAC的基本用法勘伺,熟練這幾個(gè)以后,我們很多場(chǎng)景都能運(yùn)用自如褂删,而且會(huì)發(fā)現(xiàn)RAC真的很方便飞醉。
三、RACSignal使用
其實(shí)在RAC中最核心的類RACSiganl
,搞定這個(gè)類就能用ReactiveCocoa開(kāi)發(fā)了屯阀。
RACSiganl:信號(hào)類,一般表示將來(lái)有數(shù)據(jù)傳遞缅帘,只要有數(shù)據(jù)改變,信號(hào)內(nèi)部接收到數(shù)據(jù)难衰,就會(huì)馬上發(fā)出數(shù)據(jù)钦无。
創(chuàng)建信號(hào) & 激活信號(hào) & 廢棄信號(hào)
// 1.創(chuàng)建信號(hào) + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
// 2.訂閱信號(hào),才會(huì)激活信號(hào). - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.發(fā)送信號(hào) - (void)sendNext:(id)value
// 4.廢棄信號(hào) RACDisposable
// 創(chuàng)建信號(hào)
RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào),就會(huì)調(diào)用block盖袭。
// 發(fā)送信號(hào)
[subscriber sendNext:@1];
// 如果不在發(fā)送數(shù)據(jù)失暂,最好發(fā)送信號(hào)完成,內(nèi)部會(huì)自動(dòng)調(diào)用[RACDisposable disposable]取消訂閱信號(hào)鳄虱。
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// 銷毀信號(hào)
// block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤弟塞,就會(huì)自動(dòng)執(zhí)行這個(gè)block,取消訂閱信號(hào)。
// 執(zhí)行完Block后拙已,當(dāng)前信號(hào)就不在被訂閱了决记。
NSLog(@"信號(hào)銷毀");
}];
}];
// 訂閱信號(hào),才會(huì)激活信號(hào).
[siganl subscribeNext:^(id x) {
NSLog(@"接到數(shù)據(jù)x=%@",x);
}];
/*
2015-12-21 15:47:18.335 ReactiveCocoaLearn[82287:4789675] 接到數(shù)據(jù)x=1
2015-12-21 15:47:18.335 ReactiveCocoaLearn[82287:4789675] 信號(hào)銷毀
*/
信號(hào)的處理
3-1、map
[[self.testTextField.rac_textSignal map:^id(NSString *textStr){
return @(textStr.length);
}] subscribeNext:^(id x){
NSLog(@"x==%@",x);
}];
// 映射
3-2倍踪、filter
[[[self.testTextField.rac_textSignal map:^id(NSString *textStr){
return @(textStr.length);
}] filter:^BOOL(NSNumber * value){
return value.integerValue > 2;
}] subscribeNext:^(id x){
NSLog(@"x==%@",x);
}];
過(guò)濾掉一部分
3-3系宫、delay
RACSignal *siganl = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"realySendSignal");
[subscriber sendNext:@1];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"discard Signal");
}];
}] delay:3];
NSLog(@"SubscriSiganl");
[siganl subscribeNext:^(id x) {
NSLog(@"recevieSiganl=%@",x);
}];
// 延遲3秒才接收數(shù)據(jù)
/*
2015-12-21 16:33:05.326 ReactiveCocoaLearn[83488:4831881] 開(kāi)始預(yù)訂信號(hào)
2015-12-21 16:33:05.327 ReactiveCocoaLearn[83488:4831881] 真正發(fā)送信號(hào)
2015-12-21 16:33:05.328 ReactiveCocoaLearn[83488:4831881] 銷毀信號(hào)
2015-12-21 16:33:08.621 ReactiveCocoaLearn[83488:4831881] 接收信號(hào)=1
*/
注意打印的時(shí)間,發(fā)送信號(hào)建车,訂閱信號(hào) 的時(shí)間扩借,再次了解下整個(gè)流程。
3-4癞志、startWith
RACSignal *siganl = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"one"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}] startWith:@"two"];
[siganl subscribeNext:^(id x) {
NSLog(@"接收信號(hào)=%@",x);
}];
// 2015-12-21 16:38:27.160 ReactiveCocoaLearn[83642:4836850] 接收信號(hào)=two
// 2015-12-21 16:38:27.162 ReactiveCocoaLearn[83642:4836850] 接收信號(hào)=one
相當(dāng)于在發(fā)送某個(gè)信號(hào)之前先發(fā)送另一個(gè)信號(hào)
3-5往枷、timeout
RACSignal *siganl = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 假設(shè)某個(gè)請(qǐng)求的時(shí)間用了幾秒
[[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{
[subscriber sendNext:@"one"];
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
// NSLog(@"銷毀信號(hào)");
}];
// 然后timeout就是當(dāng)超過(guò)這個(gè)時(shí)間的時(shí)候就會(huì)出錯(cuò)
}] timeout:10.0 onScheduler:[RACScheduler mainThreadScheduler]];
[siganl subscribeNext:^(id x){
NSLog(@"x==%@",x);
} error:^(NSError * error){
// 這個(gè)地方就很容易來(lái)處理錯(cuò)誤的時(shí)候啦
NSLog(@"error==%@",[error description]);
} completed:^{
NSLog(@"completed");
}];
比較適合用于 請(qǐng)求超時(shí)的時(shí)候
3-6、take & skip & takeLast
RACSignal *siganl = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"one"];
[subscriber sendNext:@"two"];
[subscriber sendNext:@"three"];
[subscriber sendNext:@"four"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}] take:2];
[siganl subscribeNext:^(id x){
NSLog(@"x==%@",x);
}];
//take 只接收前幾次
//skip 跳過(guò)前幾次
//takeLast 只接收最后幾次
/ *
takeUntilBlock:
takeWhileBlock:
skipWhileBlock:
skipUntilBlock:
*/
四凄杯、進(jìn)階使用
在我們向服務(wù)器進(jìn)行請(qǐng)求的時(shí)候错洁,RAC為我們帶來(lái)了諸多方便的事情,值得探索戒突。
此處還是用DeveloperLx的例子屯碴,textFiled舉例說(shuō)明。
4-1膊存、throttle
[[self.testTextField.rac_textSignal throttle:0.5]subscribeNext:^(id x){
NSLog(@"%@", x);
}];
就是在我們?cè)O(shè)置那個(gè)時(shí)間內(nèi)(0.5秒)导而,不會(huì)發(fā)送消息忱叭,讓其不會(huì)一直不斷的發(fā)送過(guò)來(lái)。
4-2 distinctUntilChanged
[[[self.testTextField.rac_textSignal throttle:0.5] distinctUntilChanged]subscribeNext:^(id x){
NSLog(@"%@", x);
}];
相同的就不發(fā)送今艺,直到有所該變?cè)侔l(fā)送
4-3 ignore
[[[[self.testTextField.rac_textSignal throttle:0.5] distinctUntilChanged] ignore:@""] subscribeNext:^(id x){
NSLog(@"%@", x);
}];
忽略某個(gè)值韵丑,像上面就是忽略 空值
4-4 switchToLatest
先綜合了下 map
[[[[[[self.testTextField.rac_textSignal throttle:0.5] distinctUntilChanged] ignore:@""] map:^id(id value){
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>subscriber){
[subscriber sendNext:value];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{}];
}];
}]switchToLatest ]subscribeNext:^(NSString * x){
NSLog(@"x==%@", x);
}];
只執(zhí)行最后一次,這個(gè)地方有待推敲虚缎,暫時(shí)還不是很理解
4-5 merge
RACSignal * signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"Signal_A"];
[subscriber sendCompleted];
});
return nil;
}];
RACSignal * signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"Signal_B"];
[subscriber sendCompleted];
});
return nil;
}];
NSLog(@"開(kāi)始預(yù)訂");
[[RACSignal merge:@[signalA, signalB]]subscribeNext:^(id x) {
NSLog(@"x==%@",x);
}];
/*
2015-12-21 17:54:24.105 ReactiveCocoaLearn[85576:4905054] 開(kāi)始預(yù)訂
2015-12-21 17:54:26.306 ReactiveCocoaLearn[85576:4905054] x==Signal_A
2015-12-21 17:54:27.398 ReactiveCocoaLearn[85576:4905054] x==Signal_B
*/
同時(shí)訂閱信號(hào)
4-6 concat
NSLog(@"開(kāi)始預(yù)訂");
[[RACSignal concat:@[signalA, signalB]]subscribeNext:^(id x) {
NSLog(@"x==%@",x);
}];
/*
2015-12-21 17:57:03.718 ReactiveCocoaLearn[85651:4908056] 開(kāi)始預(yù)訂
2015-12-21 17:57:05.720 ReactiveCocoaLearn[85651:4908056] x==Signal_A
2015-12-21 17:57:09.012 ReactiveCocoaLearn[85651:4908056] x==Signal_B
*/
執(zhí)行完A 后才執(zhí)行 B 撵彻,而且A必須成功,B才會(huì)執(zhí)行实牡,他們是異步請(qǐng)求.
4-7陌僵、zipwith
NSLog(@"開(kāi)始預(yù)訂");
[[signalA zipWith:signalB] subscribeNext:^(id x) {
NSLog(@"x==%@",x);
}];
/*
2015-12-21 18:01:18.770 ReactiveCocoaLearn[85742:4913279] 開(kāi)始預(yù)訂
2015-12-21 18:01:22.071 ReactiveCocoaLearn[85742:4913279] x==<RACTuple: 0x7f8cc8c2c520> (
"Signal_A",
"Signal_B"
)
*/
注意看上面返回的時(shí)間差距
返回一個(gè)RACTuple(元祖) ,A创坞、B 至少都發(fā)送過(guò)一次消息后,才返回碗短。
三者以上的可以用下面這個(gè),combineLatest,同上
[[RACSignal combineLatest:@[signalA,signalB,signalC]] subscribeNext:^(id x){
NSLog(@"x==%@",x);
}];
五题涨、RAC常見(jiàn)宏
5.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):
用于給某個(gè)對(duì)象的某個(gè)屬性綁定
RAC(self.testButton, backgroundColor) = [RACObserve(self.testButton, selected) map:^UIColor *(NSNumber * selected) {
return [selected boolValue] ? [UIColor redColor] : [UIColor greenColor];
}];
[[self.testButton rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(UIButton * btn) {
btn.selected = !btn.selected;
}];
直接改變button 的顏色
5.2 RACObserve(self, name):
監(jiān)聽(tīng)某個(gè)對(duì)象的某個(gè)屬性,返回的是信號(hào)
[RACObserve(self.greenView, center) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
點(diǎn)擊按鈕偎谁,改變其center之后
/*
2015-12-21 18:18:52.229 ReactiveCocoaLearn[86031:4931305] NSPoint: {0, 0}
2015-12-21 18:18:54.024 ReactiveCocoaLearn[86031:4931305] 按鈕被點(diǎn)擊了
2015-12-21 18:18:54.025 ReactiveCocoaLearn[86031:4931305] NSPoint: {187.5, 333.5}
*/
下面這個(gè)也是同樣的用這個(gè)宏的,這是用最少的代碼寫一個(gè)秒表纲堵。
RAC(self.testLabel, text) = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] map:^NSString *(NSDate * date) {
return date.description;
}];
總的來(lái)說(shuō)搭盾,記錄的筆記大致差不多了,有很多東西自己還沒(méi)深入了解婉支,畢竟我還沒(méi)運(yùn)用在項(xiàng)目中鸯隅,初次記錄,慢慢學(xué)習(xí)吧向挖。再次還是非常感謝DeveloperLx蝌以,讓我了解RAC的這么好用的東東,后期繼續(xù)探索中何之,暫時(shí)記錄到此跟畅。
備注:
DeveloperLx 的github和微博,在此。
另外參考了下列文章:
http://www.reibang.com/p/87ef6720a096
http://southpeak.github.io/blog/2014/08/02/reactivecocoazhi-nan-%5B%3F%5D-:xin-hao/