函數響應式編程及ReactiveObjC學習筆記 (二)
之前我們初步認識了RAC的設計思路跟實現方式, 現在我們再來看看如果使用它以及它能幫我們做什麽
One of the major advantages of RAC is that it provides a single, unified approach to dealing with asynchronous behaviors, including delegate methods, callback blocks, target-action mechanisms, notifications, and KVO.
官方是這樣說的, RAC為我們提供了簡單便捷實現代理 / block回調 / 事件 / 通知 / KVO的方式
我們先看RAC如何幫助我們快速實現KVO
首先我們新建一個Student類, 給它一個age的屬性
#import <Foundation/Foundation.h> @interface Student : NSObject @property (nonatomic, strong) NSString *age; @end
下面我們看一個簡單的如何使用RAC來實現KVO
Student *stu = [[Student alloc] init]; // RAC KVO [RACObserve(stu, age) subscribeNext:^(id_Nullable x) { NSLog(@"stu的age改變為: %@", x); }]; stu.age = @"10";
運行看看:
2017-07-23 11:35:19.704 RAC[67362:13075201] stu的age改變為: (null) 2017-07-23 11:35:19.704 RAC[67362:13075201] stu的age改變為: 10
很方便對吧, 不用我們去add observe, 不用出來觀察事件, 也不用我們去移除關註
不過大家註意到了沒, 這裏添加關註後block立即執行了一次, 大家可以依據實際項目情況加個條件判斷做處理.
這裏其實RAC還為我們提供了除了subscriber以外的操作, 後面再介紹給, 現在我們主要先來看RAC是怎麽替我們做KVO的
我們再看看RAC如何幫我們實現target-action
我們創建一個項目, 在controller中添加一個button, 然後給button添加一個點擊事件
如果是常規寫法的話, 在創建完button後創建一個點擊響應方法, 然後通過addTarget把響應方法跟button及事件綁定到一起
大概類似這樣:
[button addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside]; - (void)btnAction { NSLog(@"點擊了按鈕"); }
在上一篇我們提到過這樣的劣勢, 當代碼比較多的時候結構容易亂, 維護的時候也不好查找方法
我們看看RAC如何幫我們優雅的實現
RAC為我們提供了rac_signalForControlEvents來處理UIControllerEvent, 我們試試看
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { NSLog(@"%@", x); }];
因為不知道這裏的x是什麽, 我們直接打印看看結果
2017-07-23 12:05:59.654 RAC[67611:13189769] <UIButton: 0x7f95e0d069f0; frame = (157 350.5; 100 35); opaque = NO; layer = <CALayer: 0x6080000269a0>>
當我們點擊按鈕打印了上面這些, 是我們創建的button對象
那麽加入需要點擊的時候給button更換背景圖片或者標題就可以在這裏處理了, 我們用改變顏色舉例
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { x.backgroundColor = [UIColor redColor]; }];
運行後, 就可以看到如果點擊按鈕背景就會變成紅色, 如果有點擊事件也可以放在這裏
但如果點擊後要處理的邏輯比較多, 代碼超過三行建議大家單獨寫一個方法供調用, 以免破壞代碼的結構
RAC這樣的使用方式, 讓我的代碼邏輯更加清晰緊湊了, 我們再看一個添加手勢的例子
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init]; [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) { NSLog(@"點擊了屏幕"); NSLog(@"x: %@", x); }]; [self.view addGestureRecognizer:tap];
運行看看:
2017-07-23 12:15:59.246 RAC[67766:13231274] 點擊了屏幕 2017-07-23 12:15:59.247 RAC[67766:13231274] x: <UITapGestureRecognizer: 0x6000001a5160; state = Ended; view = <UIView 0x7fb932d03780>; target= <(action=sendNext:, target=<RACPassthroughSubscriber 0x60800003a920>)>>
這裏x是一個手勢, 我們可以直接拿來使用, 比如我們改變下添加手勢這個view的顏色
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init]; [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) { NSLog(@"點擊了屏幕"); NSLog(@"x: %@", x); x.view.backgroundColor = [UIColor redColor]; }]; [self.view addGestureRecognizer:tap];
這樣手勢的初始化, 方法等等都在一起, 讓代碼一目了然
接下來我們看看RAC如何幫我們實現通知的
我們常規的通知應該是這樣, 在要接收通知的地方添加關註通知並寫上通知事件
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notiAction) name:@"noti" object:nil]; - (void)notiAction { NSLog(@"接到了通知"); }
然後在對應的地方發送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti" object:nil userInfo:nil];
RAC會怎麽幫我們實現呢?
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"noti" object:nil] subscribeNext:^(NSNotification * _Nullable x) { NSLog(@"接到了通知"); }];
發送通知iOS已經很簡單了, RAC沒有做重復工作但幫我們把添加關註通知的方法改進了, 可以讓事件和通知關註在一起
這樣接口就清晰了
那麽RAC如果幫我們實現代理呢?
我用UIAlertView給大家舉個例子, 雖然蘋果已經不推薦用這個 不過我們拿來當例子用用看
先寫一個常規的AlertView
#import "ViewController.h" #import <ReactiveObjC.h> @interface ViewController ()<UIAlertViewDelegate> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"RAC Delegate Test" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil]; [alertView show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 0) { NSLog(@"點擊了Cancel按鈕"); } else { NSLog(@"點擊了Ok按鈕"); } } @end
初始化alertView, 實現代理方法 這是我們常規的用法
那麽我們再看看RAC如何做
#import "ViewController.h" #import <ReactiveObjC.h> @interface ViewController ()<UIAlertViewDelegate> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"RAC Delegate Test" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil]; [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * _Nullable x) { NSLog(@"%@", x); }]; [alertView show]; } @end
RAC為我們提供了一個rac_signalForSelector: fromProtoc方法幫我們實現代理
我們把x打印看看
2017-07-23 12:53:07.138 RAC[68380:13356332] <RACTuple: 0x6080000149f0> ( "<UIAlertView: 0x7fc7dfc0c620; frame = (0 0; 0 0); layer = <CALayer: 0x6000002218a0>>", 1 )
我們看看這個RACTuple
@interface RACTuple : NSObject <NSCoding, NSCopying, NSFastEnumeration> @property (nonatomic, readonly) NSUInteger count; /// These properties all return the object at that index or nil if the number of /// objects is less than the index. @property (nonatomic, readonly, nullable) id first; @property (nonatomic, readonly, nullable) id second; @property (nonatomic, readonly, nullable) id third; @property (nonatomic, readonly, nullable) id fourth; @property (nonatomic, readonly, nullable) id fifth; @property (nonatomic, readonly, nullable) id last;
打印看看
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"RAC Delegate Test" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil]; [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * _Nullable x) { NSLog(@"%@", x); NSLog(@"first: %@", x.first); NSLog(@"second: %@", x.second); NSLog(@"third: %@", x.third); NSLog(@"fourth: %@", x.fourth); NSLog(@"fifth: %@", x.fifth); NSLog(@"last: %@", x.last); }]; [alertView show];
結果為:
2017-07-23 16:29:26.089 RAC[68525:13409884] first: <UIAlertView: 0x7f814e604420; frame = (0 0; 0 0); layer = <CALayer: 0x60800003a3e0>> 2017-07-23 16:29:26.090 RAC[68525:13409884] second: 1 2017-07-23 16:29:26.090 RAC[68525:13409884] third: (null) 2017-07-23 16:29:26.090 RAC[68525:13409884] fourth: (null) 2017-07-23 16:29:26.091 RAC[68525:13409884] fifth: (null) 2017-07-23 16:29:26.091 RAC[68525:13409884] last: 1
第一個是alert本身, 第二個是index, 然後可以按我們的需要做處理了
另外要註意的是用RAC寫代理是有局限的,它只能實現返回值為void的代理方法
先到這裏, 現在我們知道我們能用RAC做什麽了
下次我們繼續看RAC的具體用法
函數響應式編程及ReactiveObjC學習筆記 (二)