ReactiveCocoa Documents 翻译(基于版本V2.5)
1. 基本操作(Basic Operators)描述 ReactiveCocoa 最常用的一些操作以及使用范例。 主要是如何运用 序列(sequences) 和 信号(signals) 的流操作。 用信号实现副作用(Performing side effects with signals)
流的传输(Transforming streams)
流的结合(Combining streams)
信号的结合(Combining signals)
1.1 用信号实现副作用(Performing side effects with signals)译者注:什么是冷信号,什么是热信号?
译者注:什么是push-driven? 什么是pull-driven?
大多数信号(signals)初始化是冷(cold)信号,这意味着它们在被订阅(Subscription)之前不会做任何工作。 副作用: 当调用函数时,除了返回函数值之外,还对主调用函数产生附加影响,这就叫函数的副作用。 ReactiveCocoa 的函数参数是 In/Out 作用的参数,即函数可能改变参数里面的的内容,把一些信息通过输入参数,夹带到外界。 这种情况严格来说也是副作用,是非纯函数。我们所讨论的函数式反应式编程中的函数式编程属于非纯函数,它是有副作用的。 1.1.1 订阅(Subscription)
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; // 输出: A B C D E F G H I [letters subscribeNext:^(NSString *x) { NSLog(@"%@",x); }]; 对于冷信号来说,副作用会在每一次订阅时发生: __block unsigned subscriptions = 0; RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { subscriptions++; [subscriber sendCompleted]; return nil; }]; // 输出: // subscription 1 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u",subscriptions); }]; // 输出: // subscription 2 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u",subscriptions); }]; 行为可以被 1.1.2 注入影响(Injecting effects)
__block unsigned subscriptions = 0; RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { subscriptions++; [subscriber sendCompleted]; return nil; }]; // 没有任何输出 loggingSignal = [loggingSignal doCompleted:^{ NSLog(@"about to complete subscription %u",subscriptions); }]; // 输出: // about to complete subscription 1 // subscription 1 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u",subscriptions); }]; 1.2 流的转换(Transforming streams)下面的操作将流转换为一个新的流。 1.2.1 映射(Mapping)
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; // Contains: AA BB CC DD EE FF GG HH II RACSequence *mapped = [letters map:^(NSString *value) { return [value stringByAppendingString:value]; }]; 1.2.2 过滤(Filtering)
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: 2 4 6 8 RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) { return (value.intValue % 2) == 0; }]; 1.3 流的合并(Combining streams)下面的操作合并多个流到一个单一的新流。 1.3.1 串联(Concatenating)
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 RACSequence *concatenated = [letters concat:numbers]; 1.3.2 扁平(Flattening,这个真不知道怎么翻译)
序列是串联(concatenated)的 RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; RACSequence *sequenceOfSequences = @[ letters,numbers ].rac_sequence; // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 RACSequence *flattened = [sequenceOfSequences flatten]; 信号是合并(merged)的: RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { [subscriber sendNext:letters]; [subscriber sendNext:numbers]; [subscriber sendCompleted]; return nil; }]; RACSignal *flattened = [signalOfSignals flatten]; // Outputs: A 1 B C 2 [flattened subscribeNext:^(NSString *x) { NSLog(@"%@",x); }]; [letters sendNext:@"A"]; [numbers sendNext:@"1"]; [letters sendNext:@"B"]; [letters sendNext:@"C"]; [numbers sendNext:@"2"]; 1.3.3 映射和扁平(Mapping and flattening)
可用来扩展或编辑序列: RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 RACSequence *extended = [numbers flattenMap:^(NSString *num) { return @[ num,num ].rac_sequence; }]; // Contains: 1_ 3_ 5_ 7_ 9_ RACSequence *edited = [numbers flattenMap:^(NSString *num) { if (num.intValue % 2 == 0) { return [RACSequence empty]; } else { NSString *newNum = [num stringByAppendingString:@"_"]; return [RACSequence return:newNum]; } }]; 或者创建多个信号自动合并: RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; [[letters flattenMap:^(NSString *letter) { return [database saveEntriesForLetter:letter]; }] subscribeCompleted:^{ NSLog(@"All database entries saved successfully."); }]; 1.4 信号组合(Combining signals)下面的操作组合多个信号到一个新信号。 1.4.1 序列化(Sequencing)
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; // The new signal only contains: 1 2 3 4 5 6 7 8 9 // // But when subscribed to,it also outputs: A B C D E F G H I RACSignal *sequenced = [[letters doNext:^(NSString *letter) { NSLog(@"%@",letter); }] then:^{ return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal; }]; 这在某些情况下很有用,比如执行一个信号的所有副作用,然后开始另外一个信号,并且只返回第二个信号的值。 1.4.2 合并(Merging)
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *merged = [RACSignal merge:@[ letters,numbers ]]; // Outputs: A 1 B C 2 [merged subscribeNext:^(NSString *x) { NSLog(@"%@",x); }]; [letters sendNext:@"A"]; [numbers sendNext:@"1"]; [letters sendNext:@"B"]; [letters sendNext:@"C"]; [numbers sendNext:@"2"]; 1.4.3 组合最新值(Combining latest values)
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *combined = [RACSignal combineLatest:@[ letters,numbers ] reduce:^(NSString *letter,NSString *number) { return [letter stringByAppendingString:number]; }]; // Outputs: B1 B2 C2 C3 [combined subscribeNext:^(id x) { NSLog(@"%@",x); }]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [numbers sendNext:@"1"]; [numbers sendNext:@"2"]; [letters sendNext:@"C"]; [numbers sendNext:@"3"]; 注意结合信号会只发送第一个值,当所有输入被发送至少一个的时候。上面的例子中, 1.4.4 切换(Switching)
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSubject *signalOfSignals = [RACSubject subject]; RACSignal *switched = [signalOfSignals switchToLatest]; // Outputs: A B 1 D [switched subscribeNext:^(NSString *x) { NSLog(@"%@",x); }]; [signalOfSignals sendNext:letters]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [signalOfSignals sendNext:numbers]; [letters sendNext:@"C"]; [numbers sendNext:@"1"]; [signalOfSignals sendNext:letters]; [numbers sendNext:@"2"]; [letters sendNext:@"D"]; 2 设计指南(Design Guidelines)本文档包含如何在工程中使用 ReactiveCocoa 的设计指南。本章的内容重度参考了Rx Design Guidelines。 本文假设读者熟悉 ReactiveCocoa 的基本功能。
最佳实践
完成新operators
2.1 RACSequence的约定(The RACSequence contract)
2.1.1 (运算默认是懒执行模式)Evaluation occurs lazily by default序列运算默认是懒执行模式,如下面的序列: NSArray *strings = @[ @"A",@"B",@"C" ]; RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { return [str stringByAppendingString:@"_"]; }]; 没有字符串会被实际追加直到序列真正需要的时候。 访问 这通常能避免不必要的工作(因为不需要的值不会被计算),但意味着序列是要处理的。 一旦被计算,序列中的值就被存储不会被重新计算。访问 如果懒式运算模式不可取 - 例如,因为内存有限的时候,较少使用内存更重要 - eagerSequence 属性可能被强制转为饥渴模式。 2.1.2 运算会阻塞调用者(Evaluation blocks the caller)不管序列是懒模式还是饥渴模式,运算序列的任何部分都会阻塞调用者线程直到任务完成。阻塞是必须的因为值必须从序列中同步返回。 如果运算序列序列的代价大到可能阻塞线程很明显的时间,考虑用 2.1.3 副作用只发生一次(Side effects occur only once)当传递给序列操作的 block 引发了副作用,要明白副作用对每个值只会发生一次,就是在值被运算的时候。 NSArray *strings = @[ @"A",@"C" ]; RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { NSLog(@"%@",str); return [str stringByAppendingString:@"_"]; }]; // Logs "A" during this call. NSString *concatA = sequence.head; // Logs "B" during this call. NSString *concatB = sequence.tail.head; // Does not log anything. NSString *concatB2 = sequence.tail.head; RACSequence *derivedSequence = [sequence map:^(NSString *str) { return [@"_" stringByAppendingString:str]; }]; // Still does not log anything,because "B_" was already evaluated,and the log // statement associated with it will never be re-executed. NSString *concatB3 = derivedSequence.tail.head; 2.2 RACSignal 约定(The RACSignal contract)
2.2.1 信号事件是串行的(Signal events are serialized)信号可以在任何线程中分发事件。连续的事件甚至被允许分发到不同的线程或者调度者,除非显示的指定了分发到特定的调度者。 然而,RAC 不会有两个信号并发到达。一个事件被处理时,不会有另外的事件被分发。其他事件的发送会被强制等待直到当前事件被处理完成。 特别注意,这意味着传递给 2.2.2 订阅总会在调度的时候发生(Subscription will always occur on a scheduler)要保证 如果的订阅者的线程已经有一个 参见文档 2.2.3 错误会立即传送出来(Errors are propagated immediately)在 RAC中,
2.2.4 副作用发生在每次订阅时(Side effects occur for each subscription)对 考虑如下代码: __block int aNumber = 0; // Signal that will have the side effect of incrementing `aNumber` block // variable for each subscription before sending it. RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { aNumber++; [subscriber sendNext:@(aNumber)]; [subscriber sendCompleted]; return nil; }]; // This will print "subscriber one: 1" [aSignal subscribeNext:^(id x) { NSLog(@"subscriber one: %@",x); }]; // This will print "subscriber two: 2" [aSignal subscribeNext:^(id x) { NSLog(@"subscriber two: %@",x); }]; 副作用会在每次订阅的时候重复发生。同样适用于 __block int missilesToLaunch = 0; // Signal that will have the side effect of changing `missilesToLaunch` on // subscription. RACSignal *processedSignal = [[RACSignal return:@"missiles"] map:^(id x) { missilesToLaunch++; return [NSString stringWithFormat:@"will launch %d %@",missilesToLaunch,x]; }]; // This will print "First will launch 1 missiles" [processedSignal subscribeNext:^(id x) { NSLog(@"First %@",x); }]; // This will print "Second will launch 2 missiles" [processedSignal subscribeNext:^(id x) { NSLog(@"Second %@",x); }]; 要阻止上述行为,在多次订阅一个信号时只执行它的副作用一次,可以用信号的多播功能 2.2.5 订阅会在完成和错误的时候自动释放(Subscriptions are automatically disposed upon completion or error)当一个 参见文档 2.2.6 Disposal取消正在进行的工作和清理资源(Disposal cancels in-progress work and cleans up resources)订阅被释放的时候,不管手动或自动,任何正在处理或与订阅相关的工作会尽快被取消,订阅相关的资源会被释放。 2.3 Best practices下面的建议有助于保证基于 RAC 的代码可预测,可理解和高效。 然而,仅仅只是指导。判断是否遵循了建议的标准是下面的代码片段。 2.3.1 为返回信号的属性和方法使用描述性的声明(Use descriptive declarations for methods and properties that return a signal)方法或属性如果返回 有三个关键问题必须在声明中表达清楚:
没有副作用的热信号应该典型的用属性来代替方法。使用属性意味着在订阅信号的时间之前不需要初始化,并且额外的订阅不会改变语义。 信号属性应该以事件命名(例如 没有副作用的冷信号应该从名词命名的方法中返回(例如: 带副作用的信号应该被动词命名的方法返回(例如: 2.3.2 始终缩进流操作(Indent stream operations consistently)如果没有合适的格式化,流代码和容易变得密集和混乱。使用缩进能够清晰的看出来链式流操作的开始和结束。 调用流的简单的方法时,不需要要额外的缩进。: RACStream *result = [stream startWith:@0]; RACStream *result2 = [stream map:^(NSNumber *value) { return @(value.integerValue + 1); }]; 如果传输同一个流多次,确保每一个步骤都是对齐的。 复杂的操作比如 RACStream *result = [[[RACStream zip:@[ firstStream,secondStream ] reduce:^(NSNumber *first,NSNumber *second) { return @(first.integerValue + second.integerValue); }] filter:^ BOOL (NSNumber *value) { return value.integerValue >= 0; }] map:^(NSNumber *value) { return @(value.integerValue + 1); }]; 当然,带block参数的嵌套的流应该跟block一起自然缩进: [[signal then:^{ @strongify(self); return [[self doSomethingElse] catch:^(NSError *error) { @strongify(self); [self presentError:error]; return [RACSignal empty]; }]; }] subscribeCompleted:^{ NSLog(@"All done."); }]; 2.3.3 对流的所有的值使用相同的类型(Use the same type for all the values of a stream)
任何可能的时候,流都应该只包含相同类型的对象。 2.3.4 避免长时间持有流(Avoid retaining streams for too long)保留
参见 2.3.5 只处理需要数量的流(Process only as much of a stream as needed)让流或者 如果流中只有特定数量的值需要被用到, 类似 2.3.6 分发信号事件到一个已知的调度(Deliver signal events onto a known scheduler)当信号被一个方法返回,或者被信号组合,很难搞清楚是在哪个线程上事件被分发。 尽管事件被确保是串行的,但有时候需要更严格的情形,比如 UI 的刷新必须在主线程。 无论何时保证事件是串行的都很重要, 2.3.7 (较少的场合需要切换调度者)Switch schedulers in as few places as possible在满足上面的情况下,事件还应该在必要的时候分发到明确的 通常,使用 2.3.8 明确信号的副作用(Make the side effects of a signal explicit)
然而,有时候信号时间发生时,副作用是有用的。 尽管大多数 NSMutableArray *nexts = [NSMutableArray array]; __block NSError *receivedError = nil; __block BOOL success = NO; RACSignal *bookkeepingSignal = [[[valueSignal doNext:^(id x) { [nexts addObject:x]; }] doError:^(NSError *error) { receivedError = error; }] doCompleted:^{ success = YES; }]; RAC(self,value) = bookkeepingSignal; 2.3.9 用多播共享信号副作用(Share the side effects of a signal by multicasting)默认情况下,副作用在每次订阅的时候发生,但在某些特定的情况下副作用应够只发生一次--例如, 一个网络请求很明显不应该在添加新的订阅的时候重复调用。
// This signal starts a new request on each subscription. RACSignal *networkRequest = [RACSignal createSignal:^(id<RACSubscriber> subscriber) { AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation,id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; } failure:^(AFHTTPRequestOperation *operation,NSError *error) { [subscriber sendError:error]; }]; [client enqueueHTTPRequestOperation:operation]; return [RACDisposable disposableWithBlock:^{ [operation cancel]; }]; }]; // Starts a single request,no matter how many subscriptions `connection.signal` // gets. This is equivalent to the -replay operator,or similar to // +startEagerlyWithScheduler:block:. RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]]; [connection connect]; [connection.signal subscribeNext:^(id response) { NSLog(@"subscriber one: %@",response); }]; [connection.signal subscribeNext:^(id response) { NSLog(@"subscriber two: %@",response); }]; 2.3.10 通过给定的名字调试流(Debug streams by giving them names)每一个 例如如下代码片段: RACSignal *signal = [[[RACObserve(self,username) distinctUntilChanged] take:3] filter:^(NSString *newUsername) { return [newUsername isEqualToString:@"joshaber"]; }]; NSLog(@"%@",signal); 上面的代码会记录一个类似 名称也可以通过
2.3.11 避免明确的订阅和释放(Avoid explicit subscriptions and disposal)尽管 同样的,明确的使用 下面是几乎总是该遵循的高级模式,用来替换手动订阅和释放:
通常,相比在订阅的回调中完成相同功能,使用 2.3.12 尽可能避免使用 subjects(Avoid using subjects when possible)
因为 Subjects 可以在任何地方任何时间使用,所以 subjects 经常打破 stream 的线性处理,导致逻辑复杂。 Subjects 也不支持严格的 disposal,严格的 disposal 会引入不必要的任务。 Subjects 能够被 ReactiveCocoa 的下列其他模式替换:
当 subject 必须使用时,他们几乎总是被使用在信号链的基本输入,而不是在信号链中间使用。 2.4 实现一个新的操作(Implementing new operators)RAC 为 实现新的操作需要特别注意一些细节和简单化操作,避免在调用的代码中引入bug。 下面的指南包括一些通用的原则能够帮助编写符合预期的 API: 2.4.1 优先使用基于 RACStream 的方法(Prefer building on RACStream methods)
基于这个原因,无论何时新操作都应该基于 如果一个新的 2.4.2 尽可能组合已存在的操作(Compose existing operators when possible)RAC 经过了深思熟虑,通过了合法性测试,也在很多项目中被使用。重新改写操作的代码可能不会有很好的健壮性,或者不能处理一些内建操作已经考虑到的特殊情况。 为了最小的重复代码和尽可能少的引入 bug,在自定义的操作实现中,尽可能使用已提供的功能。通常只有很少的代码需要重写。 2.4.3 避免引入并发(Avoid introducing concurrency)在编程中,并发是非常容易引入 bug 的。为了尽可能的避免死锁和竞态,不应该引入并发。 调用者可以在一个明确的 2.4.4 在 disposable 中取消任务和清理所有资源(Cancel work and clean up all resources in a disposable)使用
2.4.5 操作中不要阻塞(Do not block in an operator)流操作应该立即返回一个新流。任何操作需要完成的工作应该是新流的运算的一部分,而不是调用的流自身的一部分。 // WRONG! - (RACSequence *)map:(id (^)(id))block { RACSequence *result = [RACSequence empty]; for (id obj in self) { id mappedObj = block(obj); result = [result concat:[RACSequence return:mappedObj]]; } return result; } // Right! - (RACSequence *)map:(id (^)(id))block { return [self flattenMap:^(id obj) { id mappedObj = block(obj); return [RACSequence return:mappedObj]; }]; } 如果要从流中返回一个或多个值(例如 2.4.6 避免深度递归导致栈溢出(Avoid stack overflow from deep recursion)任何无限递归操作都需要使用 例如,下面是 - (RACSignal *)repeat { return [RACSignal createSignal:^(id<RACSubscriber> subscriber) { RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; __block void (^resubscribe)(void) = ^{ RACDisposable *disposable = [self subscribeNext:^(id x) { [subscriber sendNext:x]; } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{ resubscribe(); }]; [compoundDisposable addDisposable:disposable]; }; return compoundDisposable; }]; } 而下面的版本就会避免栈溢出: - (RACSignal *)repeat { return [RACSignal createSignal:^(id<RACSubscriber> subscriber) { RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; RACScheduler *scheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler]; RACDisposable *disposable = [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) { RACDisposable *disposable = [self subscribeNext:^(id x) { [subscriber sendNext:x]; } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{ reschedule(); }]; [compoundDisposable addDisposable:disposable]; }]; [compoundDisposable addDisposable:disposable]; return compoundDisposable; }]; } 3 与 Rx 的差异(Differences from Rx)ReactiveCocoa (RAC) 深受 .NET 的 一些不同之处,像方法和类的命名,是为了符合 Cocoa 现有的风格。 还有一些是在 Rx 上的改进,或者从其他函数式响应式编程范式中借鉴的(例如 Elm 编程语言)。 下面,尝试讲解 RAC 和 Rx 的不同。 3.1 接口(Interfaces)EAC 不提供类似 .NET 中的
3.2 流操作的名称(Names of Stream Operations)RAC 通常使用 LINQ 风格命名流方法。大多数异常处理深受 Haskell 和 Elm 的影响。 注意如下不同:
LINQ 操作在 RAC 中名称有变化(但行为或多或少相同),在文档中有记载,例如: // Maps `block` across the values in the receiver. // // This corresponds to the `Select` method in Rx. // // Returns a new stream with the mapped values. - (instancetype)map:(id (^)(id value))block; 4 框架概览(Framework Overview)本文包含 ReactiveCocoa 框架不同组件的一些高层次的描述,并且尝试说明他们如何在一起工作,然后分别承担什么职责。 这意味着要先理解本文,然后才学习其他新模块和其他特定文档。 范例和如何使用 RAC,参见 4.1 流(Streams)
值可以立即获得,也可以在未来某个时间获得,但必须是按顺序获取。在没有计算或等到第一个值之前是无法获取第二个值的。 流是游离的(monads,游牧的)?还允许基于一些基本操作构建复杂的操作(特别是
4.2 信号(Signals)signal,由 信号通常用来表示未来可能会被分发的数据。当任务完成或者数据被接受,值会被发送到信号,信号则推送他们到任何订阅者。 用户必须订阅(subscribe)信号才能访问信号的值。 信号提供给订阅者三种不同类型的事件:
信号的生命周期由任意数量的 4.2.1 订阅(Subscription)订阅者是唯一能从信号中等待或者能够从信号中等待事件的主体。在 RAC 中,订阅是任何遵从 订阅在 订阅会保留他们订阅的信号,不会自动释放除非信号完成或者出错。订阅也能够手动释放。 4.2.2 Subjectssubject用 Subjects 可以认为是可变的信号,类似 例如,处理应用逻辑的block,可以用发送事件到共享 subject 的 block 代替。 subject 随后返回一个 某些 subject 提供额外的功能。 4.2.3 命令(Commands)command用 通常行为触发命令是由 UI 驱动的,例如一个按钮被按下。 基于信号的命令能够自动被禁用,这个禁用状态能够用 UI 中其他任何跟命令相关的控件来描述。 在 OS X 中,RAC 添加了一个 4.2.4 连接(Connections)连接用 信号默认是cold,意味着他们在每一次新订阅者添加的时候开始工作。 这个行为通常是期望的,因为数据会为每个订阅者刷新和重新计算,但是如果信号有副作用或者任务代价很昂贵就会引入一些问题 (例如发送网络请求)。 连接通过 4.3 序列(Sequences)序列用 序列是一个集合,累世完成 序列类似 RAC 添加了 4.4 释放(Disposables)RACDisposable类用来取消任务和资源清理。 Disposables 常用于信号的取消订阅。 当订阅被释放,响应的订阅者不会再收到信号的任何未来事件。 并且任何订阅相关的工作(后台处理,网络请求等)都会取消,因为结果不再需要了。 关于取消的更多信息,参见 RAC 4.5 调度(Schedulers)调度用 调度类似 GCD 队列,但调度支持取消,并且总是串行执行。
4.6 值类型(Value types)RAC 提供少量杂项类来方便表达流中的值:
5. 内存管理(Memory Management)ReactiveCocoa 的内存管理非常复杂,但最终结果是处理信号时,你并不需要保留他们。 如果框架要求你保留每一个信号,那这个框架使用起来就太笨重,特别是一次性的信号用于未来某个时候。 你不需要保留任何长时间活跃的信号到属性中,然后确保用完之后再清除。这样可没劲了。 5.1 订阅者(Subscribers)无论去哪之前, 5.2 有限或短暂的信号(Finite or Short-Lived Signals)RAC 内存管理最重要的原则是订阅在完成后错误时自动终止,订阅者会被移除 例如,如果你的 view controller 中的代码如下: self.disposable = [signal subscribeCompleted:^{ doSomethingPossiblyInvolving(self); }]; … the memory management will look something like the following: 那么内存管理会是下面的流程: view controller -> RACDisposable -> RACSignal -> RACSubscriber -> view controller 然而, 这是你需要的,因为 5.3 无限信号(Infinite Signals)Infinite signals (or signals that live so long that they might as well be infinite),however,will never tear down naturally. This is where disposables shine. Disposing of a subscription will remove the associated subscriber,and just 无限信号(或者说永远存活的信号),不会被自动清除。 释放订阅会移除相关的订阅者,并且会清除订阅相关的任何资源。 作为一个一般的经验法则,如果你需要手动管理订阅的生命周期,那可能存在更好的方式做到你想要的,请避免显示的订阅和释放。 5.4 从
|
- XML 转solr schema filed name
- 使用Swift的CocoaPods 0.36上的GoogleAnalytics-iOS-SDK
- PostgreSQL数据字典查询[持续更新]
- Cocos2d-X3.0 刨根问底(四)----- 内存管理源码分析
- 开源DMS - 文档管理系统 - logicaldoc 里面转换SWF
- oracle – SQL Developer太慢了 我还能用什么?
- ruby-on-rails – 从部分参考其他部分的相对路径
- NoSQL数据建模技术
- flex datagrid 添加树 flex 教程 flex培训 flex源码 flex实
- 正则表达式获取字符串的input标签的属性值