美文网首页
Life is short, you need RAC.

Life is short, you need RAC.

作者: 超人猿 | 来源:发表于2018-06-06 17:17 被阅读61次

序言:

正如标题所说:人生苦短,我用RAC。
我们以往常用的代理方法、block 回调、target-action 机制、通知、KVO等等都可以用RAC代替,并且RAC的代码更精简,让人爱不释手。但不建议初学者使用,因为RAC的精简态度,让你忘记苹果原生的代码!!!

一张图纵览RAC架构
看到上面的图之后,会有一定的全局观,这里建议大家好好细读雷纯锋对RAC的源码解析的文章:ReactiveCocoa v2.5 源码解析之架构总览

这篇文章主要是实验用RAC代替以往常用的行为:代理、通知等。

一、RAC替代代理方法

以往我们在一个View上点击它的Button,会在ViewController实现它的功能,这个过程需要做代理方法,如下:

新建一个MainView类继承UIView,在其.h文件中,敲下如下代码

#import <UIKit/UIKit.h>

// 声明代理
@protocol MainViewDelegate <NSObject>

@required
- (void)btnPressed;

@end

@interface MainView : UIView

// 定义一个weak属性的delegate
@property (weak, nonatomic) id<MainViewDelegate> delegate;

@end

MainView.m

#import "MainView.h"

@implementation MainView

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        self.frame = frame;
        
        // 创建UI
        [self createUI];
        
    }
    return self;
}

// MARK:- 创建UI
- (void)createUI {
    
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width * 0.5 - 280 * 0.5, 100, 280, 30)];
    [btn setTitle:@"我是小蘑菇" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(btnPressed) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:btn];
    
    
}

// 点击Btn
- (void)btnPressed {
    
    if (_delegate && [_delegate respondsToSelector:@selector(btnPressed)]) {
        [_delegate btnPressed];
    }
    
}

@end

ViewController.m

// 导入MainView
#import "MainView.h"

// 遵守协议
@interface ViewController () <MainViewDelegate>

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    // 代理方法
    [self delegateTest];
}

// MARK:- 代理方法
- (void)delegateTest {
    // MainView实例化
    MainView *mainView = [[MainView alloc] initWithFrame:self.view.frame];
    mainView.delegate = self;
    [self.view addSubview:mainView];
}
// MARK:- <MainViewDelegate>
- (void)btnPressed {
    self.view.backgroundColor = [UIColor orangeColor];
}
@end

程序Run后,点击按钮,当前view的backgroundColor会随之改变。但是有没有发现以上的步骤有点多?如果用RAC呢?

用RAC前,在你创建好的Podfile,添加pod 'ReactiveObjC', '~> 3.0.0',接着pod install

以上操作完毕后,进入MainView.h文件,修改如下

#import <UIKit/UIKit.h>

//@protocol MainViewDelegate <NSObject>
//
//@required
//- (void)btnPressed;
//
//@end

@interface MainView : UIView

//@property (weak, nonatomic) id<MainViewDelegate> delegate;

@end

MainView.m的修改

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        self.frame = frame;
        
        // 创建UI
        [self createUI];
        
    }
    return self;
}

// MARK:- 创建UI
- (void)createUI {
    
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width * 0.5 - 280 * 0.5, 100, 280, 30)];
    [btn setTitle:@"我是小蘑菇" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
   // [btn addTarget:self action:@selector(btnPressed) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:btn];
    
    
}

// - (void)btnPressed {
    
//    if (_delegate && [_delegate respondsToSelector:@selector(btnPressed)]) {
//        [_delegate btnPressed];
//    }
    
// }

接着进入ViewController.m文件,不需要遵守协议,只在要实现的方法加入以下代码:


// 导入ReactiveObjC.h
#import "ReactiveObjC.h"

// 实现方法
- (void)delegateTest {
    MainView *mainView = [[MainView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:mainView];
    [[mainView rac_signalForSelector:@selector(btnPressed)] subscribeNext:^(RACTuple * _Nullable x) {

        NSLog(@"%@", [NSThread currentThread]);
        self.view.backgroundColor = [UIColor orangeColor];

    }];

}

替代代理方法实验效果成功。这里说下:1.subscribeNext block下返回的是主线程,2.不用担心循环引用问题,RAC已经帮我们考虑好了。

二、监听点击事件

MainView.h,添加这一句

@property (strong, nonatomic) UIButton *btn;

在MainView.m 的createUI的方法中,添加并修改👇🏻

// MARK:- 创建UI
- (void)createUI {
    
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width * 0.5 - 280 * 0.5, 100, 280, 30)];
    [btn setTitle:@"我是小蘑菇" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//    [btn addTarget:self action:@selector(btnPressed) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:btn];
    
    self.btn = btn;
}

ViewController.m

  MainView *mainView = [[MainView alloc] initWithFrame:self.view.frame];
   [self.view addSubview:mainView];
  [[mainView.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"x: %@", x);
    }];

点击事件实验成功,是不是很精简?

三、替代KVO

如果需要了解KVO可以看 杨萧玉的《Objective-C中的KVC和KVO》
基于以上,在ViewController.m文件添加如下代码

- (void)kvoTest {
   MainView *mainView = [[MainView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:mainView];

    // 点击按钮
    [[mainView.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        mainView.backgroundColor = [UIColor redColor];
    }];
    
    // KVO 记得移除KVO监听backgroundColor属性
    [mainView addObserver:self forKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionOld context:nil];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"监听到了%@的%@属性发生了改变", object, keyPath);
    NSLog(@"%@", change);
}

上面我们设定点击按钮后改变mainVie的颜色,并且用KVO监听了背景颜色,运行后:

这里用RAC代替可这样修改:

- (void) kvoTest {
    
    MainView *mainView = [[MainView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:mainView];

    // 点击按钮
    [[mainView.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        mainView.backgroundColor = [UIColor redColor];
    }];
    
    // KVO
//    [mainView addObserver:self forKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionOld context:nil];
    
    // RAC 这里是不需要做移除操作的
    [[mainView rac_valuesForKeyPath:@"backgroundColor" observer:self] subscribeNext:^(id  _Nullable x) {
        if (x) {
            NSLog(@"%@",x);
        }
    }];
    
}

//- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//    NSLog(@"监听到了%@的%@属性发生了改变", object, keyPath);
//    NSLog(@"%@", change);
//}

实验效果完成!

当然了,如果你的项目是用了MVVM模式,可以在 MVVM 中使用 RACSubject 实现统一的错误处理逻辑。比如,我们可以在 viewModel 的基类中声明一个 RACSubject 类型的属性 errors ,然后在 viewController 的基类中编写统一的错误处理逻辑:

[self.viewModel.errors subscribeNext:^(NSError *error) {
    // 错误处理逻辑
}

采摘上面推荐的雷纯锋的原话,有修改

只要你肯想象,RAC的威力不止这些,期待大家的分享,有机会可以一起探讨🌹

相关文章

网友评论

      本文标题:Life is short, you need RAC.

      本文链接:https://www.haomeiwen.com/subject/uqlbsftx.html