1. 程式人生 > >函數響應式編程及ReactiveObjC學習筆記 (二)

函數響應式編程及ReactiveObjC學習筆記 (二)

per 舉例 def nsobject uibutton 為我 string method 寫法

之前我們初步認識了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學習筆記 (二)