1

一些常用的RACSignal

如果你没有听说和使用过ReactiveCocoa框架,请阅读sunnyxx写的入门教程

本文将罗列一些常用的RACSignal方法,并会不断更新。

RAC()和RACObserve()

RAC(<#TARGET, ...#>)宏用来将一个对象的属性和信号量绑定,
RACObserve(<#TARGET#>, <#KEYPATH#>)宏则用来生成一个对象的绑定属性的信号量,
这样描述很抽象,上一个例子解释

@property (nonatomic, strong) NSString* testText;
@property (nonatomic, strong) NSString* testText_2;

RAC(self,testText) = RACObserve(self, testText_2);

这个例子中,testText_2属性有任何变动,都会通知给testText变成一样的内容,testText变化不会影响testText_2。RAC宏在等号左边,右边RACObserve宏返回一个传值信号量。

这个用法十分宽泛,可以省去自己添加KVO或添加分散且复杂的绑定代码,并且逻辑更直观。

RACSingal

接着我们讲讲RACSignal这个信号量类,他的基类是RACStream,从名字看出,"信号量类"继承自"流类",所以RACSignal支持一些高级,如:flattenMapflattenmap等等,您看不会用也没关系,本篇文章我们用不到这些函数。

这些函数的用法请参考使用ReactiveCocoa实现iOS平台响应式编程博文,此文中使用的RACSequence类和RACSingal同样继承自RACStream,是RAC框架的数组类,RACSequenceNSArray转换也十分简单:

NSArray* array = @[@(1),@(2),@(3),@(4),@(5)];
RACSequence* sequence = [array rac_sequence];  //NSArray -> RACSequence
NSArray* array2 = [sequence array];  //RACSequence -> NSArray

既然RACSignalRACSequence都是"流"类,那肯定有共同的特性。RACSequence队列,是很形象的流,例如人流车流都是队列。RACSingal则抽象一点,所谓信号,也是一种流,我们用代码来描述。

 RACSignal* getRandomSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       
        [subscriber sendNext:@(arc4random())];
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    [getRandomSignal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    [getRandomSignal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
2015-12-29 10:28:20.611 Fahu[967:365178] 707130803
2015-12-29 10:28:20.613 Fahu[967:365178] 743304158

我们的getRandomSignal信号,是一个用来获取随机数的信号量,用类方法createSignal初始化,subscriber则是这个信号量的订阅者,当有人订阅这个信号量时,[subscriber sendNext:@(arc4random())];信号量返回一个随机数给订阅者,注意这个createSignal的Block,只有在有人订阅时才会执行。

订阅的操作就是信号量调用subscribeNext方法,一次订阅会返回一个值,再订阅一次,返回了一个新的随机数,如上方LOG。

代码并不难懂,出于讲解需要,写了这么一个例子,并不是很合理。应为直接调用arc4random就可以获取随机数,没必要写这么麻烦把它包装成信号量。但随着一项任务的复杂程度增高,信号量写法便会展示出更清晰的逻辑。

举个例子来源于ReactiveCocoa的开源项目,天气类软件Tropos。代码中包含了一些高级函数,请根据注释来理解

先普及一下CLLocationManager的回调,这个位置管理器类,开始定位以后,会调用locationManager:didUpdateLocations:locationManager:didFailWithError:两个回调,前者用来返回定位数据NSArray<CLLocation*>,后者用来返回错误。

//返回一个用来更新地理位置的信号量
- (RACSignal *)updateCurrentLocation
{
//已知[self didUpdateLocations]是locationManager:didUpdateLocations:回调的信号量,这个回调返回定位的GPS信息。[self didUpdateLocations]返回的GPS位置数组信号量,进行map操作,map操作简单的说就是遍历+处理,[self didUpdateLocations],每每返回一个位置数组,这一步的map函数都将其变为数组的最后一个对象输出,然后再用filter函数过滤,把请求时间超时的数据去掉,isStale是一个私有属性,这里就不粘出来了。
    RACSignal *currentLocationUpdated = [[[self didUpdateLocations] map:^id(NSArray *locations) {
        return locations.lastObject; //返回最后一个对象
    }] filter:^BOOL(CLLocation *location) {
        return !location.isStale; //过滤超时对象
    }];
//map函数返回id类型,用来替换。filter函数返回 BOOL值,决定要不要过滤掉,返回NO就过滤。

//locationManager:didFailWithError:的信号量
    RACSignal *locationUpdateFailed = [[[self didFailWithError] map:^id(NSError *error) {
        return [RACSignal error:error];
    }] switchToLatest];
    
//最后返回的信号量,是上面两股信号量的merge(混合),take:1是不管这两个信号那一个先来,只返回一个,initially是信号量开始时候调用的block,finally则是信号量结束了调用的block。
    return [[[[RACSignal merge:@[currentLocationUpdated, locationUpdateFailed]] take:1] initially:^{
        [self.locationManager startUpdatingLocation];
    }] finally:^{
        [self.locationManager stopUpdatingLocation];
    }];
}

不知道您感觉如何,相对于传统的delegate方法使用CLLocationManagerRAC信号量是不是让整个获取逻辑更加清晰明了,用一个函数完成了之前最少两段控制代码和两个回调的工作。

rac_signalForSelector

再让我们看看上段代码中的didUpdateLocations信号量怎么来的

- (RACSignal *)didUpdateLocations
{
    return [[self rac_signalForSelector:@selector(locationManager:didUpdateLocations:) fromProtocol:@protocol(CLLocationManagerDelegate)] reduceEach:^id(CLLocationManager *manager, NSArray *locations) {
        return locations;
    }];
}

selfCLLocationManagerDelegate,是CLLocationManager的委托,通过rac_signalForSelector生成了@selector(locationManager:didUpdateLocations:)的信号量,这个信号量在被订阅后,会在locationManager:didUpdateLocations:回调调用的时候,向订阅者发出信号。并且使用reduceEach函数把回调中多余的信息删除了,只返回了位置信息。

rac_signalForSelector这个方法,既可以返回普通Selector的信号量,也可以像上例中返回代理方法的信号量,十分好用。

RACSignal类簇

RACSignal的设计模式,就是标准的类簇模式,不同的Signal都是继承于RACSignal的子类,但是都封装起来不用开发者关注,和NSArray、UIButton的设计模式相同。

例如下面这个简单的返回定位状态授权的信号量,使用的[RACSignal return:@(authorized)]生成的便是RACReturnSignal类信号量。

- (RACSignal *)authorized
{
    BOOL authorized = [self authorizationStatusEqualTo:kCLAuthorizationStatusAuthorizedWhenInUse] || [self authorizationStatusEqualTo:kCLAuthorizationStatusAuthorizedAlways];
    return [RACSignal return:@(authorized)];
}
+ (RACSignal *)return:(id)value {
    return [RACReturnSignal return:value];
}

秋刀生鱼片
2.1k 声望82 粉丝

独立游戏开发者