美文网首页
iOS RAC基础用法、RACCommand、RACTuple、

iOS RAC基础用法、RACCommand、RACTuple、

作者: Harry__Li | 来源:发表于2021-04-12 17:57 被阅读0次

前面我们说了信号的创建以及它内部实现的一个逻辑。这篇文章开始说一下RAC的场景用法。

Button 监听方法

按照我们系统的button的写法

[self.testbutton addTarget:self action:@selector(testClick:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)testClick:(UIButton *)sender{
    
}

就像上面的代码,给按钮添加点击事件,如果不实现方法还会报错奔溃。当然也可以用xib或者storyboard来实现。再来看看RAC是怎么实现的。

//通过rac监听按钮
    [[self.testbutton rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"按钮输出的是:%@",x);
    }];

一行代码搞定,不用担心没实现方法奔溃,很省心啊。我们前面说了,创建信号 发送消息,订阅信号,但是在这没有发送消息,那这个订阅block是怎么触发的。点进方法看看

- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
    @weakify(self);

    return [[RACSignal
        createSignal:^(id<RACSubscriber> subscriber) {
            @strongify(self);

            [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];

            RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
                [subscriber sendCompleted];
            }];
            [self.rac_deallocDisposable addDisposable:disposable];

            return [RACDisposable disposableWithBlock:^{
                @strongify(self);
                [self.rac_deallocDisposable removeDisposable:disposable];
                [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
            }];
        }]
        setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", RACDescription(self), (unsigned long)controlEvents];
}

这个方法 创建一个信号,而在block里面,我们看到[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; 这个给订阅者添加了发送消息的方法,而下面创建信号的销毁,在信号销毁的时候移除发送消息方法。这也就可以解释为什么,在button监听中不需要发送消息。
当然除了上面的监听方法,还有其他的

 [[self.testbutton rac_signalForSelector:@selector(testClick:)]subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"输出的:%@",x);
    }];
监听textfield输入

不适用Rac,平常我们添加textfield内容变化,可以用系统给的delegate也可以给textfield添加方法 查看内容的变化,都很麻烦。现在我们来看看使用rac的代码

 [self testTextField];

-(void)testTextField{
    [[self.numtextfield rac_textSignal]subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"输入的是:%@",x);
       self.titlelabel.text=x;
    }];
}

真的是一句代码搞定。获取到内容我们就可以在block里面操作。你觉得这样就很简单了嘛。当然不是,rac还有更加简单的。

RAC(self.titlelabel,text)=self.numtextfield.rac_textSignal;

上面的代码同样也能达到我们的结果。
RAC(对象,对象的属性)=(一个信号);
比如RAC(btn,enable)=(RACSignal) 按钮的ennable等于一个信号的

 //可以选择想要的状态监听 这里是输入完之后
    [[self.numtextfield rac_signalForControlEvents:UIControlEventEditingDidEnd]subscribeNext:^(__kindof UIControl * _Nullable x) {
        //x 就是numtextfield本身
        UITextField *textField=x;
        NSLog(@"输入的是:%@",textField.text);
    }];
//添加条件--输入文字长度>10时,调用订阅block
    [[self.numtextfield.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
            return value.length>10;
        }]subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"输出的是:%@",x);
    }];

这里我们用到一个fillter 这个是过滤,我们把它放在后面说。上面就是textfield的一些基本使用。

KVO

在之前我们的时候,kvo是像下面这样创建的

 [self.titlelabel addObserver:self forKeyPath:@"text"  options:(NSKeyValueObservingOptionNew) context:nil];
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([keyPath isEqualToString:@"text"]) {
        NSLog(@"---%@",change);
    }
}
-(void)dealloc{
    
    [self removeObserver:self forKeyPath:@"text"];
}

先要添加观察,然后在观察对象发生变化时,做出各种操作。当添加多个观察时,需要根据keypath来判断。最后还需要移除观察者。这样写就很麻烦。
下面我们来看看RAC是怎么实现的

 [[_titlelabel rac_valuesForKeyPath:@"text" observer:self]subscribeNext:^(id  _Nullable x) {
        NSLog(@"----%@",x);
    }];

再来个简单的

 [RACObserve(_titlelabel, text) subscribeNext:^(id  _Nullable x) {
            NSLog(@"--rac:--%@",x);
    }];

相比我们原来创建观察,rac简单多了。

通知

我们先来看看,不用rac是怎么实现的
现在一个界面创建

[[NSNotificationCenter defaultCenter]postNotificationName:@"TestNoti" object:nil userInfo:@{@"name":@"test"}];

然后接受

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(noti:) name:@"TestNoti" object:nil];
}
-(void)noti:(NSNotification *)noti{
     
}
最后再delloc中移除
[[NSNotificationCenter defaultCenter]removeObserver:self];

而使用RAC的话,我们可以减少add的代码,同时也不需要在delloc中移除通知

 [[[NSNotificationCenter defaultCenter]rac_addObserverForName:@"TestNoti" object:nil]subscribeNext:^(NSNotification * _Nullable x) {
       
        NSDictionary *dict=x.userInfo;
        NSLog(@"传递参数是:%@",dict);
    }];
代理

先来看看不用Rac是怎么写的 总共六步

@protocol TestDelegate <NSObject>

@optional
-(void)testNameWith:(NSString *_Nullable)name;

@end
@interface SecondVC : UIViewController

@property (weak, nonatomic) id<TestDelegate > ldelegate;

@end

-(IBAction)btnclick:(UIButton *)sender{
    
    if ([self.ldelegate respondsToSelector:@selector(testNameWith:)]) {
        [self.ldelegate testNameWith:@"123"];
    }

上面之写了前三步骤,但看着很繁琐。那么,使用rac又是什么效果呢

//创建信号
@property (strong, nonatomic) RACSubject *btnSubject;
//初始化
-(RACSubject *)btnSubject{
    if (!_btnSubject) {
        _btnSubject=[RACSubject subject];
    }
    return _btnSubject;
}
//在传递参数
- (IBAction)registerBtnClick:(UIButton *)sender {
    [_btnSubject sendNext:@"传递参数"];
}
//订阅信号
[logintVC.btnSubject subscribeNext:^(id  _Nullable x) {
       
        NSLog(@"接受的参数是:%@",x);
    }];

现在看看,rac是不是简单多了。

RACCommand

RACCommand在RAC中是对一个动作的触发条件以及它产生的出发事件的封装。
触发条件:初始化RACCommand的入参enabledSignal就决定了是否能开始执行
触发事件:signalblock就是对触发事件的封装。

-(void)testCommend{
    
    RACCommand *command=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
       
        NSLog(@"输出为:%@",input);
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            [subscriber sendNext:@"猪八戒胖了胖"];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"销毁了");
            }];
        }];
    }];
    
    [command execute:@"发送消息了1111"];
}
打印结果:LBDaySurgery(Dev)[14495:5446321] 输出为:发送消息了1111

上面我们能拿到传递的参数@"发送消息了1111",但是 [subscriber sendNext:@"猪八戒胖了胖"];发送的消息,我们怎么拿到呢。

  [[command execute:@"发送消息了1111"]subscribeNext:^(id  _Nullable x) {
    
        NSLog(@"=======%@",x);
    }];
2021-04-12 15:40:43.425053+0800 LBDaySurgery(Dev)[14540:5453960] 输出为:发送消息了1111
2021-04-12 15:40:43.438798+0800 LBDaySurgery(Dev)[14540:5453960] =======猪八戒胖了胖

我们添加了订阅信号,点击进execute方法,就能发现,它返回的是一个信号。既然是信号那就能订阅。最后也成功的打印出内容。
除了上面的方式还有其他的方法可以打印出

  RACCommand *command=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
       
        NSLog(@"输出为:%@",input);
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            [subscriber sendNext:@"猪八戒胖了胖"];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"销毁了");
            }];
        }];
    }];
    
//    [[command execute:@"发送消息了1111"]subscribeNext:^(id  _Nullable x) {
//
//        NSLog(@"=======%@",x);
//    }];
    
    [command.executionSignals subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------%@",x);
        [x subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    }];
    [command execute:@"666 666"];
    
04-12 15:47:48.573818+0800 LBDaySurgery(Dev)[14546:5456154] 输出为:666 666
2021-04-12 15:47:48.587024+0800 LBDaySurgery(Dev)[14546:5456154] -------<RACDynamicSignal: 0x281487b60> name:
2021-04-12 15:47:48.587084+0800 LBDaySurgery(Dev)[14546:5456154] 猪八戒胖了胖

executionSignals就是用来发送信号的信号源,需要注意的是这个方法一定要在执行execute方法之前,否则就不起作用了.
上面的方法觉得很麻烦,在来看个实现简单的。

RACCommand *command=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
       
        NSLog(@"输出为:%@",input);
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            [subscriber sendNext:@"猪八戒胖了胖"];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"销毁了");
            }];
        }];
    }];
    
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
       
        NSLog(@"++++++%@",x);
    }];
    
    [command execute:@"666 666"];

上面这种是不是简练多了。但是 出现了一个switchToLatest,这是有什么用的呢。
switchToLatest:用于信号中信号,通过switchToLatest回去最新的信号。
具体我来段代码就明白了

-(void)testswitchToLatest{
    
    RACSubject *subject1=[RACSubject subject];
    RACSubject *subject2=[RACSubject subject];
    RACSubject *subject3=[RACSubject subject];
    
    [subject1.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"-----输出是:%@",x);
    }];
    
    [subject1 sendNext:subject2];
    [subject1 sendNext:subject3];
    
    [subject3 sendNext:@"这个是3"];
    [subject2 sendNext:@"这个是2"];
    
}

91953+0800 LBDaySurgery(Dev)[14676:5484247] -----输出是:这个是3

最后加入的信号是sendNext:subject3,所以它打印出来的就是3的消息参数

RACTuple

RACTuple就是把oc的数组进行了一层封装。
创建方法:

-(void)testRACTuple{
    
    [RACTuple tupleWithObjects:@"123",@666,@"123",@"djiue de", nil];
    [RACTuple tupleWithObjectsFromArray:@[@"得救的就",@"99999"]];
    [RACTuple tupleWithObjectsFromArray:@[@"232131",@"333334",@"444453"] convertNullsToNils:YES];
}
点进去查看tonil这个是是什么
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
    for (id object in array) {
        [newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];
    }

用来判断是否为空以及可以做的操作。

 RACTuple *tuple=[RACTuple tupleWithObjectsFromArray:@[@"232131",@"333334",@"444453"] convertNullsToNils:YES];
    NSLog(@"+++++%@ -----%@=====%@",tuple[0],tuple.first,tuple.last);

 打印出:LBDaySurgery(Dev)[14697:5497499] +++++232131 -----232131=====444453

对照NSArray数组的操作,其实都是类似的。

RACSequence

1.可用for in 枚举
2.rac对oc的集合和RACTuple进行Category扩充,可用集合. rac_sequence,把集合快速的转成RACSequence对象。
3.订阅RACSequence的signal,可遍历所有元素。但内部实现是异步的,所以需要注意时间顺序。
下面来看看具体的实现

-(void)testRACSequence{
    
    NSArray *array=@[@"12",@"34",@"56"];
    NSDictionary *dict=@{@"name":@"小李子",@"age":@"长生不死",@"socre":@"无穷大"};
    NSString *str=@"abc";
    NSSet *set=[NSSet setWithArray:array];
    RACTuple *tuple=[RACTuple tupleWithObjectsFromArray:array];
    
    //nsarray
    [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"遍历数组输出的是:%@",x);
    }];
    //字典
    [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"字典输出的是---:%@",x);
    }];
    //字符串遍历
    [str.rac_sequence.signal subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"字符串:%@",x);
    }];
    //集合
    [set.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"集合:%@",x);
    }];
    [tuple.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"元祖:%@",x);
    }];
    
    
}

按照oc的遍历来看,最后打印出来的应该是按顺序的。我们来看下是不是

021-04-12 17:53:27.374930+0800 LBDaySurgery(Dev)[14715:5506387] 遍历数组输出的是:12
2021-04-12 17:53:27.374996+0800 LBDaySurgery(Dev)[14715:5506387] 遍历数组输出的是:34
2021-04-12 17:53:27.375021+0800 LBDaySurgery(Dev)[14715:5506387] 遍历数组输出的是:56
2021-04-12 17:53:27.375081+0800 LBDaySurgery(Dev)[14715:5506389] 元祖:12
2021-04-12 17:53:27.375093+0800 LBDaySurgery(Dev)[14715:5506390] 字符串:a
2021-04-12 17:53:27.375116+0800 LBDaySurgery(Dev)[14715:5506389] 元祖:34
2021-04-12 17:53:27.375145+0800 LBDaySurgery(Dev)[14715:5506389] 元祖:56
2021-04-12 17:53:27.375199+0800 LBDaySurgery(Dev)[14715:5506391] 集合:12
2021-04-12 17:53:27.375256+0800 LBDaySurgery(Dev)[14715:5506383] 字典输出的是---:<RACTwoTuple: 0x282f9f420> (
    name,
    "\U5c0f\U674e\U5b50"
)
2021-04-12 17:53:27.375273+0800 LBDaySurgery(Dev)[14715:5506390] 字符串:b
2021-04-12 17:53:27.375283+0800 LBDaySurgery(Dev)[14715:5506391] 集合:34
2021-04-12 17:53:27.375385+0800 LBDaySurgery(Dev)[14715:5506390] 字符串:c
2021-04-12 17:53:27.375475+0800 LBDaySurgery(Dev)[14715:5506383] 字典输出的是---:<RACTwoTuple: 0x282f91950> (
    age,
    "\U957f\U751f\U4e0d\U6b7b"
)
2021-04-12 17:53:27.375542+0800 LBDaySurgery(Dev)[14715:5506391] 集合:56
2021-04-12 17:53:27.375641+0800 LBDaySurgery(Dev)[14715:5506383] 字典输出的是---:<RACTwoTuple: 0x282f918c0> (
    socre,
    "\U65e0\U7a77\U5927"
)

很明显不是按顺序来的,还记得上面的第3条嘛,它内部是异步执行的。所以它的顺序不是我们想的那样 从上到下执行的。

相关文章

网友评论

      本文标题:iOS RAC基础用法、RACCommand、RACTuple、

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