ReactiveCocoa 学习笔记二十三(Operations)

Operations

Operations 是 ReactiveCocoa 框架中 RACSignal 的扩展,其定义了许多方法来扩展信号流的使用。

实例方法

  1. 向已经存在的信号流中注入额外的操作

    - (RACSignal<ValueType> *)doNext:(void (^)(ValueType _Nullable x))block RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal<ValueType> *)doError:(void (^)(NSError * _Nonnull error))block RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal<ValueType> *)doCompleted:(void (^)(void))block RAC_WARN_UNUSED_RESULT;
    

    这三个方法十分类似,并且十分简单,只是创建了一个新的信号流,这个新的信号流在被订阅时,会去订阅当前源信号流,在订阅前会执行传入的 block 任务。

    但要注意的是,该方法创建的信号流是冷信号,如果想立刻执行额外添加的任务,那么应该订阅一下返回的信号流,以触发该信号流。

    - (RACSignal *)doNext:(void (^)(id x))block {
    	NSCParameterAssert(block != NULL);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		return [self subscribeNext:^(id x) {
    			block(x);
    			[subscriber sendNext:x];
    		} error:^(NSError *error) {
    			[subscriber sendError:error];
    		} completed:^{
    			[subscriber sendCompleted];
    		}];
    	}] setNameWithFormat:@"[%@] -doNext:", self.name];
    }
    
  2. 舍弃或延迟信号量的传递

    throttle: 方法返回一个新的信号流,该信号流会接收当前信号流的信号量,但是其会等待 interval 秒,在该时间段内若接收到新的信号量,那么则抛弃前一个信号量,继续等待 interval 秒,直到在该时间段内未接收到新的信号量,那么便转发这个最近一次接收到的信号量。

    - (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id next))predicate {
    	NSCParameterAssert(interval >= 0);
    	NSCParameterAssert(predicate != nil);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
    
    		RACScheduler *scheduler = [RACScheduler scheduler];
    
    		__block id nextValue = nil;
    		__block BOOL hasNextValue = NO;
    		RACSerialDisposable *nextDisposable = [[RACSerialDisposable alloc] init];
    
    		void (^flushNext)(BOOL send) = ^(BOOL send) {
    			@synchronized (compoundDisposable) {
    				[nextDisposable.disposable dispose];
    
    				if (!hasNextValue) return;
    				if (send) [subscriber sendNext:nextValue];
    
    				nextValue = nil;
    				hasNextValue = NO;
    			}
    		};
    
    		RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
    			RACScheduler *delayScheduler = RACScheduler.currentScheduler ?: scheduler;
    			BOOL shouldThrottle = predicate(x);
    
    			@synchronized (compoundDisposable) {
    				flushNext(NO);
    				if (!shouldThrottle) {
    					[subscriber sendNext:x];
    					return;
    				}
    
    				nextValue = x;
    				hasNextValue = YES;
    				nextDisposable.disposable = [delayScheduler afterDelay:interval schedule:^{
    					flushNext(YES);
    				}];
    			}
    		} error:^(NSError *error) {
    			[compoundDisposable dispose];
    			[subscriber sendError:error];
    		} completed:^{
    			flushNext(YES);
    			[subscriber sendCompleted];
    		}];
    
    		[compoundDisposable addDisposable:subscriptionDisposable];
    		return compoundDisposable;
    	}] setNameWithFormat:@"[%@] -throttle: %f valuesPassingTest:", self.name, (double)interval];
    }
    

    throttle: 方法是调用了上面的方法,只是参数 predicate 参数是直接返回 YES 的代码块,该代码块是用来判断是否对接收到的信号量执行延迟操作。

    该方法中关键的是 flushNext() 回调代码,首先其会执行 dispose 方法,清空仍在等待执行的任务,即抛弃上一个信号量,而后将该信号量加入等待转发的线程队列中去,这个队列一般都是主队列。

    另外,如果接收到错误信号量,那么直接转发错误信号量,如果接收到结束信号量,那么会立即转发存在的信号量,而后结束。

  3. 延迟信号量的传递

    - (RACSignal<ValueType> *)delay:(NSTimeInterval)interval RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流会延迟转发当前源信号流所传递的普通信号量以及结束信号量,但是不会延迟错误信号量。

  4. 重新订阅信号流

    - (RACSignal<ValueType> *)repeat RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流与普通的信号流不同,当其接收到当前源信号流传递过来的结束信号量时,订阅者被清理,此时,其会重新订阅源信号流,如果源信号流继续传递信号量了,那么,其就能够继续接收到信号量了。

    static RACDisposable *subscribeForever (RACSignal *signal, void (^next)(id), void (^error)(NSError *, RACDisposable *), void (^completed)(RACDisposable *)) {
    	next = [next copy];
    	error = [error copy];
    	completed = [completed copy];
    
    	RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
    
    	RACSchedulerRecursiveBlock recursiveBlock = ^(void (^recurse)(void)) {
    		RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
    		[compoundDisposable addDisposable:selfDisposable];
    
    		__weak RACDisposable *weakSelfDisposable = selfDisposable;
    
    		RACDisposable *subscriptionDisposable = [signal subscribeNext:next error:^(NSError *e) {
    			@autoreleasepool {
    				error(e, compoundDisposable);
    				[compoundDisposable removeDisposable:weakSelfDisposable];
    			}
    
    			recurse();
    		} completed:^{
    			@autoreleasepool {
    				completed(compoundDisposable);
    				[compoundDisposable removeDisposable:weakSelfDisposable];
    			}
    
    			recurse();
    		}];
    
    		[selfDisposable addDisposable:subscriptionDisposable];
    	};
    
    	recursiveBlock(^{
    		RACScheduler *recursiveScheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler];
    
    		RACDisposable *schedulingDisposable = [recursiveScheduler scheduleRecursiveBlock:recursiveBlock];
    		[compoundDisposable addDisposable:schedulingDisposable];
    	});
    
    	return compoundDisposable;
    }
    

    当 repeat 方法返回的信号流被订阅时,上面的函数会被执行,recursiveBlock() 代码块第一次被执行,直接对信号流进行订阅,而当接收到结束信号量时,会执行参数 recurse() 代码块,即

    ^{
    	RACScheduler *recursiveScheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler];
    
    	RACDisposable *schedulingDisposable = [recursiveScheduler scheduleRecursiveBlock:recursiveBlock];
    	[compoundDisposable addDisposable:schedulingDisposable];
    }
    

    但是这个代码块只会执行一次,其中调用的 scheduleRecursiveBlock: 方法负责之后接收到结束信号量后重新订阅的操作。

    需要注意的是,subscribeForever() 函数返回的清理对象被清理后,重复订阅将取消,所以接收到错误信号量后,将不再重新订阅。

  5. 初始化操作

    - (RACSignal *)initially:(void (^)(void))block {
    	NSCParameterAssert(block != NULL);
    
    	return [[RACSignal defer:^{
    		block();
    		return self;
    	}] setNameWithFormat:@"[%@] -initially:", self.name];
    }
    

    该方法返回一个信号流,该信号流每一次被订阅时,都会先执行传递的 block 任务。

  6. 结束操作

    - (RACSignal *)finally:(void (^)(void))block {
    	NSCParameterAssert(block != NULL);
    
    	return [[[self
    		doError:^(NSError *error) {
    			block();
    		}]
    		doCompleted:^{
    			block();
    		}]
    		setNameWithFormat:@"[%@] -finally:", self.name];
    }
    

    返回一个信号流,每当接收到当前源信号流传递结束或错误信号流,便执行参数 block 代码块。

  7. 批量传递信号量

    - (RACSignal<RACTuple *> *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
    

    该方法返回的信号流会缓存 interval 时间段内源信号流所传递到的信号量,并且,当时间结束时,将缓存的信号量打包为 RACTuple 信号量进行传递。当信号流结束时,会立刻将所有已保存的信号量打包传递,但是接收到错误信号量并不会传递已经保存的信号量。

  8. 接收信号流最后几个信号量

    - (RACSignal<ValueType> *)takeLast:(NSUInteger)count RAC_WARN_UNUSED_RESULT;
    

    在该方法中,每当接收到信号量便会将其保存(超过指定的数量,则删除第一个保存的信号量),直到接收到结束信号量,当前方法返回的信号流,便会将保存的所有信号量转发给订阅者。

  9. 合并信号量

    - (RACSignal *)aggregateWithStart:(id)start reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock {
        
        RACSignal *signal1 = [self scanWithStart:start reduceWithIndex:reduceBlock];
        RACSignal *signal2 = [signal1 startWith:start];
        RACSignal *signal3 = [signal2 takeLast:1];
        
        return [signal3 setNameWithFormat:@"[%@] -aggregateWithStart: %@ reduceWithIndex:", self.name, RACDescription(start)];
    }
    

    从上面的代码可知,signal1 信号流会根据参数代码块压缩源信号流所传递的信号量,并且可以设置初始值 start 。而后,该信号流会有一个前置信号量得到 signal2 信号流。而信号流 signal3 只会接收信号流 signal2 传递的最后一个信号量。所以,如果源信号流(self)并没有传递信号量,那么,该方法所返回的信号流所传递的信号量就是 start ,当然该 start 可能为 nil 。

  10. 合并信号量

    - (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock RAC_WARN_UNUSED_RESULT;
    

    该方法实际是调用了上面的方法,只是其 reduceBlock 代码块的参数并没有提供因源信号流传递信号量时产生的索引(从 0 开始)。

  11. 合并信号量

    - (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock {
    	NSCParameterAssert(startFactory != NULL);
    	NSCParameterAssert(reduceBlock != NULL);
    
    	return [[RACSignal defer:^{
    		return [self aggregateWithStart:startFactory() reduce:reduceBlock];
    	}] setNameWithFormat:@"[%@] -aggregateWithStartFactory:reduce:", self.name];
    }
    

    同上面两个方法类似,只是每当返回的信号流被订阅时,才会根据 startFactory() 返回的值作为初始值进行信号量的合并。

  12. 传递信号量集合

    - (RACSignal *)collect {
    	return [[self aggregateWithStartFactory:^{
    		return [[NSMutableArray alloc] init];
    	} reduce:^(NSMutableArray *collectedValues, id x) {
    		[collectedValues addObject:(x ?: NSNull.null)];
    		return collectedValues;
    	}] setNameWithFormat:@"[%@] -collect", self.name];
    }
    

    该方法调用了上面的方法,其初始值是一个数组,而合并操作则是将接收到的信号量加入数组,并将 nil 转换为 NSNull 实例对象。而当信号流结束传递时,返回的信号流则将该数组作为一个信号量传递出去。

  13. 合并两个信号流所传递的各自的最后一个信号量为一个信号量

    - (RACSignal<RACTwoTuple<ValueType, id> *> *)combineLatestWith:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;
    

    该方法会将两个信号流中各自最后一个信号量合并为一个 RACTwoTuple 信号量进行传递。两个信号流必须要传递过信号量,即使该信号量是 nil ,并且两者都结束时,该方法返回的信号流才会结束。但是,任何一个信号流传递流错误信号流,都会直接转发,而导致合并操作的结束。

  14. 合并信号流

    - (RACSignal *)merge:(RACSignal *)signal {
    	return [[RACSignal
    		merge:@[ self, signal ]]
    		setNameWithFormat:@"[%@] -merge: %@", self.name, signal];
    }
    

    将自己同参数 signal 信号流合并,最后得到一个信号流会转发两个信号流中所传递的信号量。

  15. 展开信号流

    - (RACSignal *)flatten:(NSUInteger)maxConcurrent RAC_WARN_UNUSED_RESULT;
    

    该方法同 flatten 类似,但是该方法会限制同时订阅信号流的个数,并且注意这里的源信号流传递的信号量是信号流类型。在该方法返回的信号流接收到源信号流所传递的所有信号量(信号流)时,订阅该信号量,如果超过当前所能订阅的信号流,那么就保存该信号流,当有订阅的信号流结束时,便从缓存中取出一个信号流进行订阅。

    直到订阅的所有信号流都结束后,该方法返回的信号流才最终结束。如果 maxConcurrent 参数为 0 ,那么该方法同 flatteb 方法效果相同,都不会限制同时订阅的信号流数量。

  16. 接替信号流

    - (RACSignal *)concat {
    	return [[self flatten:1] setNameWithFormat:@"[%@] -concat", self.name];
    }
    

    该方法和 concat: 是完全不同的两个方法,concat: 方法返回的信号流会传递源信号流和参数信号流中的信号量,并且参数信号流的信号量只有在源信号流结束传递后才会被转发,所以可以称为两个信号流的拼接。但是,concat 方法则不同,其源信号流中传递的是信号流类型的信号量,并不会在方法返回的信号流中传递。而当订阅的信号流结束后,接着订阅传递而来的下一个信号流,以此类推,直到没有可以订阅的信号流为止。

  17. 忽略所有信号量

    - (RACSignal *)ignoreValues {
    	return [[self filter:^(id _) {
    		return NO;
    	}] setNameWithFormat:@"[%@] -ignoreValues", self.name];
    }
    
  18. 当前信号流结束后再订阅指定的信号流

    - (RACSignal *)then:(RACSignal * (^)(void))block {
    	NSCParameterAssert(block != nil);
    
    	return [[[self
    		ignoreValues]
    		concat:[RACSignal defer:block]]
    		setNameWithFormat:@"[%@] -then:", self.name];
    }
    

    可见,返回的信号流忽略了当前源信号流所传递的信号量,并且拼接了一个由 block 代码块返回的信号流。

  19. 为指定的实例对象的属性绑定信号流

    - (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object;
    
    - (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(nullable id)nilValue;
    
  20. 定时发送日期,可设置最大延迟时间

    + (RACSignal<NSDate *> *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler RAC_WARN_UNUSED_RESULT;
    
    + (RACSignal<NSDate *> *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway RAC_WARN_UNUSED_RESULT;
    
  21. 接收信号量直到指定的信号流传递了非错误信号量

    - (RACSignal<ValueType> *)takeUntil:(RACSignal *)signalTrigger RAC_WARN_UNUSED_RESULT;
    

    当该方法返回的信号流会正常转发源信号流的所有信号量,所以当其接收到结束或错误信号量时,同样会导致订阅者被清理。但是,signalTrigger 信号流所传递的错误信号量则不会被接收处理。

  22. 接收信号量直到指定的信号流传递了信号量

    - (RACSignal *)takeUntilReplacement:(RACSignal *)replacement RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流会传递源信号流中的信号量,直到参数 replacement 信号流传递了信号量。此时,该方法返回的信号流转而开始传递 replacement 信号流中的信号量。

    在该方法中,有下面的代码片段,源信号流拼接了一个永远不会传递信号量的信号流,以保证源信号流传递结束信号量时,不会导致该方法返回的信号流结束。

    if (!selfDisposable.disposed) {
    	selfDisposable.disposable = [[self
    		concat:[RACSignal never]]
    		subscribe:subscriber];
    }
    
  23. 指定错误信号量的处理方式

    - (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock {
    	NSCParameterAssert(catchBlock != NULL);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		RACSerialDisposable *catchDisposable = [[RACSerialDisposable alloc] init];
    
    		RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
    			[subscriber sendNext:x];
    		} error:^(NSError *error) {
    			RACSignal *signal = catchBlock(error);
    			NSCAssert(signal != nil, @"Expected non-nil signal from catch block on %@", self);
    			catchDisposable.disposable = [signal subscribe:subscriber];
    		} completed:^{
    			[subscriber sendCompleted];
    		}];
    
    		return [RACDisposable disposableWithBlock:^{
    			[catchDisposable dispose];
    			[subscriptionDisposable dispose];
    		}];
    	}] setNameWithFormat:@"[%@] -catch:", self.name];
    }
    
    - (RACSignal *)catchTo:(RACSignal *)signal {
    	return [[self catch:^(NSError *error) {
    		return signal;
    	}] setNameWithFormat:@"[%@] -catchTo: %@", self.name, signal];
    }
    

    这两个方法返回的信号流都不会转发错误信号流,而是将该流的订阅者作为参数传递给指定的信号流的订阅方法。所以错误信息如何处理,则取决于 catchBlock 或 signal 的类型及其创建时提供的订阅处理参数。

    换言之,当源信号流传递了错误信号量时,那么就订阅参数中的信号流。

  24. 传递信号量直到出现不满足要求的信号量

    - (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock {
    	NSCParameterAssert(tryBlock != NULL);
    
    	return [[self flattenMap:^(id value) {
    		NSError *error = nil;
    		BOOL passed = tryBlock(value, &error);
    		return (passed ? [RACSignal return:value] : [RACSignal error:error]);
    	}] setNameWithFormat:@"[%@] -try:", self.name];
    }
    

    该方法返回的信号流所传递的信号量是源信号流中的信号量,但是该信号量必须在使用 tryBlock 代码块处理时返回真值。如果信号量不能通过处理,那么返回的便是错误信号量。

  25. 传递经过处理的信号量直到处理结果为空

    - (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock {
    	NSCParameterAssert(mapBlock != NULL);
    
    	return [[self flattenMap:^(id value) {
    		NSError *error = nil;
    		id mappedValue = mapBlock(value, &error);
    		return (mappedValue == nil ? [RACSignal error:error] : [RACSignal return:mappedValue]);
    	}] setNameWithFormat:@"[%@] -tryMap:", self.name];
    }
    
  26. 获取信号流中的第一个信号量

    - (nullable ValueType)firstOrDefault:(nullable ValueType)defaultValue success:(nullable BOOL *)success error:(NSError * _Nullable * _Nullable)error;
    
    - (id)first {
    	return [self firstOrDefault:nil];
    }
    
    - (id)firstOrDefault:(id)defaultValue {
    	return [self firstOrDefault:defaultValue success:NULL error:NULL];
    }
    

    该方法的返回值就是该源信号流的第一个信号量,如果信号流没有传递信号量,那么返回默认值。但是,这些方法都会阻塞当前线程。

  27. 阻塞当前线程直到信号流结束

    - (BOOL)waitUntilCompleted:(NSError **)error {
    	BOOL success = NO;
    
    	[[[self
    		ignoreValues]
    		setNameWithFormat:@"[%@] -waitUntilCompleted:", self.name]
    		firstOrDefault:nil success:&success error:error];
    
    	return success;
    }
    

    在该方法中,首先忽略源信号流的所有普通信号量,而 firstOrDefault:success:error 方法会一直阻塞直到接收到信号量。所以,直到接收到结束信号量或者错误信号量,线程才会继续执行,而 success 会返回信号流是否正常结束,error 则返回错误信息。

  28. 获取组播连接对象

    - (RACMulticastConnection *)publish {
    	RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
    	RACMulticastConnection *connection = [self multicast:subject];
    	return connection;
    }
    
    - (RACMulticastConnection *)multicast:(RACSubject *)subject {
    	[subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
    	RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
    	return connection;
    }
    

    这两个方法都返回一个组播连接,当前源信号流会被当作底层流,其只被一个转播流订阅,而其他订阅者直接订阅转播流即可,这样可以避免重复订阅源信号流时重复执行一些不必要的操作。

  29. 获取信号流中传递的最后一个信号流类型的信号量

    - (RACSignal *)switchToLatest RAC_WARN_UNUSED_RESULT;
    

    该方法返回一个信号流,该信号流所传递的信号量总是当前源信号流中所传递的信号量(信号流类型)中所传递的信号量。所以,每当源信号流发送新的信号量时,该方法返回的信号流中的信号量来源便发生了变化。

  30. 将信号流的所有信号量以集合形式返回

    - (NSArray *)toArray {
    	return [[[self collect] first] copy];
    }
    

    collect 方法返回一个信号流,该信号流只会在其源信号流结束时传递一个包含其接收到的所有信号量的数组,而 toArray 方法进一步调用了 first 方法,那么则会阻塞线程直到信号流传递了数组信号量。

  31. 获取队列

    - (RACSequence *)sequence {
    	return [[RACSignalSequence sequenceWithSignal:self] setNameWithFormat:@"[%@] -sequence", self.name];
    }
    

    该队列所包含的值来自当前源信号流中的信号量,并且如果在其没有传递信号量时获取相关的队列值,那么会阻塞线程。

  32. 获取重播信号流

    - (RACSignal *)replay {
    	RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];
    
    	RACMulticastConnection *connection = [self multicast:subject];
    	[connection connect];
    
    	return connection.signal;
    }
    
    - (RACSignal *)replayLast {
    	RACReplaySubject *subject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"[%@] -replayLast", self.name];
    
    	RACMulticastConnection *connection = [self multicast:subject];
    	[connection connect];
    
    	return connection.signal;
    }
    

    这两个方法通过将源信号流作为底层信号流,重播信号流 RACReplaySubject 对其进行转发,来达到信号流重播的效果。上面两个方法的区别在于一个没有重播信号量个数的限制,而一个只重播最后接收到的信号量。所以,当订阅上面两个方法所返回的信号流时,总会重播一下其已经接收到的源信号流所传递的信号量。

  33. 懒重播

    - (RACSignal *)replayLazily {
    	RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]];
    	return [[RACSignal
    		defer:^{
    			[connection connect];
    			return connection.signal;
    		}]
    		setNameWithFormat:@"[%@] -replayLazily", self.name];
    }
    

    replay 方法类似,但是,该方法并不会自动将重播信号流连接到源信号流上,而是,当有订阅者订阅时,才将两者连接。并且,虽然订阅两个方法返回的信号流都可以接收其以前接收到的信号量,但是 replay 方法返回的是 RACReplaySubject 类型的重播信号流,而当前方法返回的只是普通的信号流。

  34. 超时报错

    - (RACSignal<ValueType> *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler RAC_WARN_UNUSED_RESULT;
    

    订阅该方法返回的信号流时,该信号流转发源信号流的所有信号量,但是如果源信号流在指定时间内没有结束,那么该信号流则向订阅者发送一个超时错误。

  35. 指定信号量的转发的工作队列

    - (RACSignal<ValueType> *)deliverOn:(RACScheduler *)scheduler RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流会转发源信号流中传递的信号量,但是信号量的处理则是在指定的队列上进行,该方法并不会影响源信号流的相关任务的处理。

  36. 指定信号量的转发在主队列进行

    - (RACSignal<ValueType> *)deliverOnMainThread RAC_WARN_UNUSED_RESULT;
    
  37. 指定信号流的订阅操作所在的队列

    - (RACSignal<ValueType> *)subscribeOn:(RACScheduler *)scheduler RAC_WARN_UNUSED_RESULT;
    

    当订阅该方法返回的信号流时,会在指定的队列中对源信号流进行订阅。所以同上面的方法不同,它们分别设置的是订阅操作和处理信号量操作的工作队列。

  38. 获取分类信号流

    - (RACSignal<RACGroupedSignal *> *)groupBy:(id<NSCopying> _Nullable (^)(id _Nullable object))keyBlock  RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal *)groupBy:(id<NSCopying> (^)(id object))keyBlock transform:(id (^)(id object))transformBlock {
    	NSCParameterAssert(keyBlock != NULL);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		NSMutableDictionary *groups = [NSMutableDictionary dictionary];
    		NSMutableArray *orderedGroups = [NSMutableArray array];
    
    		return [self subscribeNext:^(id x) {
    			id<NSCopying> key = keyBlock(x);
    			RACGroupedSignal *groupSubject = nil;
    			@synchronized(groups) {
    				groupSubject = groups[key];
    				if (groupSubject == nil) {
    					groupSubject = [RACGroupedSignal signalWithKey:key];
    					groups[key] = groupSubject;
    					[orderedGroups addObject:groupSubject];
    					[subscriber sendNext:groupSubject];
    				}
    			}
    
    			[groupSubject sendNext:transformBlock != NULL ? transformBlock(x) : x];
    		} error:^(NSError *error) {
    			[subscriber sendError:error];
    
    			[orderedGroups makeObjectsPerformSelector:@selector(sendError:) withObject:error];
    		} completed:^{
    			[subscriber sendCompleted];
    
    			[orderedGroups makeObjectsPerformSelector:@selector(sendCompleted)];
    		}];
    	}] setNameWithFormat:@"[%@] -groupBy:transform:", self.name];
    }
    

    上面两个方法都获取一个信号流,该信号流传递的信号量是 RACGroupedSignal 类型的信号流。第一个方法实际调用了第二个方法,只是参数 transformBlock 为 nil 。其相关的步骤如下:

    1)当信号流接收到源信号流传递的信号量后,通过 keyBlock 处理信号量得到相应的 key 值。
    2)通过 key 从缓存中查找对应的 RACGroupedSignal 信号流。
    3)如果未查找到对应的 RACGroupedSignal 信号流,那么就创建一个,并保存,且将其作为信号量发送给该方法返回的信号流。
    4)使用 transformBlock 处理信号量得到结果,如果 transformBlock 不存在,就直接将信号量作为处理结果。
    5)将处理的结果作为信号量转发给 RACGroupedSignal 信号流。
    6)接收到错误信号量或结束信号量,不仅要转发给订阅者,还要转发给所有的 RACGroupedSignal 信号流。

    所以,注意上面两个方法所返回的信号流,其传递的信号量是分组信号流,而不是源信号流所传递的信号量,当然,错误和结束信号量除外。

  39. 将信号量转化为事件对象

    - (RACSignal<RACEvent<ValueType> *> *)materialize RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流会将接收到的信号量封装为 RACEvent 实例对象再进行转发,包括错误和结束信号量。

  40. 将事件对象转化为信号量

    - (RACSignal<RACEvent<ValueType> *> *)materialize RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流接收到 RACEvent 实例对象信号量后,会先将其解封为信号量再进行转发,包括错误和结束信号量。

  41. 判断信号流是否传递了信号量

    - (RACSignal<NSNumber *> *)any RAC_WARN_UNUSED_RESULT;
    

    只要源信号流传递了信号量,那么该方法返回的信号流便传递一个真值信号量,但是注意不包括错误和结束信号量。

  42. 判断信号流传递的信号量是否满足要求

    - (RACSignal<NSNumber *> *)any:(BOOL (^)(id _Nullable object))predicateBlock RAC_WARN_UNUSED_RESULT;
    

    源信号流传递的信号量若满足条件,该方法返回的信号流就传递一个真值信号量,但是注意不包括错误和结束信号量。

  43. 判断信号流传递的所有信号量是否满足要求

    - (RACSignal<NSNumber *> *)all:(BOOL (^)(id _Nullable object))predicateBlock RAC_WARN_UNUSED_RESULT;
    

    只有源信号流正常结束,并且其传递的所有信号量都满足条件,那么,该方法返回的信号流便会传递一个真值信号量。

  44. 重新订阅信号流

    - (RACSignal<ValueType> *)retry:(NSInteger)retryCount RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal<ValueType> *)retry RAC_WARN_UNUSED_RESULT;
    

    这两个方法返回的信号流接收到错误信号量时,都会重新对源信号流进行订阅,只是 retry: 方法限制流重复订阅的次数,当然,如果参数是 0 ,
    那么效果同 retry 方法一样都不会限制重复订阅的次数。

    retry: 方法同 repeat 方法一样,都调用了 subscribeForever() 函数。

  45. 采样信号流

    - (RACSignal<ValueType> *)sample:(RACSignal *)sampler RAC_WARN_UNUSED_RESULT;
    

    该方法会记录下源信号流传递的最新信号量,当采样信号流 sampler 传递了一个信号量时,保存的信号量便转发给该方法返回的信号流,如同采样一样。

    sampler 信号流发生错误或结束时,采样结束。

  46. 逻辑运算结果信号流

    - (RACSignal<NSNumber *> *)not RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal<NSNumber *> *)and RAC_WARN_UNUSED_RESULT;
    
    - (RACSignal<NSNumber *> *)or RAC_WARN_UNUSED_RESULT;
    

    上面三个方法实现了对信号流所传递的信号量执行逻辑运算,而后再传递运输结果的操作。

    • not 对信号量的值取反,而后再传递,信号量都是 NSNumber 类型。
    • and 信号量是 RACTuple 类型,对其所包含的值进行与操作,而后传递操作后得到的结果。
    • or 信号量是 RACTuple 类型,对其所包含的值进行或操作,而后传递操作后得到的结果。
  47. 执行信号量中所包含的任务

    - (RACSignal *)reduceApply RAC_WARN_UNUSED_RESULT;
    

    源信号流所传递的信号量必须是 RACTuple 类型,并且,RACTuple 所包含的第一个值必须是一个 block 任务代码,而剩下的值个数必须同任务代码块的参数个数相同。

类方法

  1. 延迟信号流的创建直到其被真正订阅

    + (RACSignal *)defer:(RACSignal<id> * (^)(void))block {
    	NSCParameterAssert(block != NULL);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		return [block() subscribe:subscriber];
    	}] setNameWithFormat:@"+defer:"];
    }
    

    该方法传递的参数 block 代码块,执行后返回目标信号流,其会主动调用订阅方法,并将真正的订阅者传递给订阅信号流时要执行的代码块(该代码块是创建信号流时传递的参数)。

    该方法完全可以将热信号流转换为冷信号流,但是要注意 block 的确是在信号流被订阅时才调用,但是 block 中返回的信号流未必就是订阅时才创建的。

  2. 合并提供的信号流中各自最后的一个信号量为一个信号量

    + (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals {
    	return [[self join:signals block:^(RACSignal *left, RACSignal *right) {
    		return [left combineLatestWith:right];
    	}] setNameWithFormat:@"+combineLatest: %@", signals];
    }
    

    该方法同 combineLatestWith: 方法类似,但是其可以指定任意个信号流进行合并,并且其是一个类方法。

  3. 合并信号流再分解合并的结果

    + (RACSignal<ValueType> *)combineLatest:(id<NSFastEnumeration>)signals reduce:(RACGenericReduceBlock)reduceBlock RAC_WARN_UNUSED_RESULT;
    

    该方法返回的信号流中所传递的信号量是提供的所有信号流的各自最后一个信号量使用 reduceBlock 代码块处理后的结果。

    这个代码块的参数必须同所提供的信号流的个数保持一致,当然最多支持 15 个。

    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wstrict-prototypes\"") \
    typedef id _Nonnull (^RACReduceBlock)();
    typedef ValueType _Nonnull (^RACGenericReduceBlock)();
    _Pragma("clang diagnostic pop")
    
  4. 合并信号流

    + (RACSignal *)merge:(id<NSFastEnumeration>)signals {
    	NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
    	for (RACSignal *signal in signals) {
    		[copiedSignals addObject:signal];
    	}
    
    	return [[[RACSignal
    		createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
    			for (RACSignal *signal in copiedSignals) {
    				[subscriber sendNext:signal];
    			}
    
    			[subscriber sendCompleted];
    			return nil;
    		}]
    		flatten]
    		setNameWithFormat:@"+merge: %@", copiedSignals];
    }
    

    合并信号流同合并信号量不同,该方法会返回一个信号流,该信号流会转发参数 signals 中包含的所有信号流中所传递的所有信号量。

  5. 获取计算结果

    + (RACSignal *)try:(id (^)(NSError **errorPtr))tryBlock {
    	NSCParameterAssert(tryBlock != NULL);
    
    	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    		NSError *error;
    		id value = tryBlock(&error);
    		RACSignal *signal = (value == nil ? [RACSignal error:error] : [RACSignal return:value]);
    		return [signal subscribe:subscriber];
    	}] setNameWithFormat:@"+try:"];
    }
    

    该方法返回一个信号流,该信号流被订阅时,会立即返回 tryBlock 代码中的任务结果,如果任务处理过程中出现错误,那么则返回该错误信息。

  6. 切换信号流 switch

    + (RACSignal<ValueType> *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(nullable RACSignal *)defaultSignal RAC_WARN_UNUSED_RESULT;
    

    该方法是 switch 语法在信号流中的使用,cases 中保存的键值对中的值是信号流类型,每当 signal 发送信号量,则在 cases 中检索对应的信号流,
    如果未检索到,则默认为 defaultSignal 信号流。

    该信号流中传递的信号量则会转发给该方法返回的信号流,而因为该方法中调用了 switchToLatest 方法,所以信号量的来源信号流是在 cases 中不断切换的。

  7. 切换信号流 if else

    + (RACSignal<ValueType> *)if:(RACSignal<NSNumber *> *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal RAC_WARN_UNUSED_RESULT;
    

    该方法是 if else 语法在信号流中的使用,信号流 boolSignal 传递的值的真假决定着该方法返回的信号流中的信号量是来自 trueSignal 信号流还是来自 falseSignal 信号流。

RACSignal 的操作可以参见示意图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值