当我创建了一个信号,并把它变成一个功能的范围,其有效保留数为0每可可约定:
RACSignal *signal = [self createSignal];
当我订阅信号,它保留了订户,并返回一个一次性的,这,每可可约定,也有一个保留的零计数。
RACDisposable *disposable = [signal subscribeCompleted:^ {
doSomethingPossiblyInvolving(self);
}];
在大多数情况下,用户将关闭在和参考self
或其实例变量或封闭范围内的其他部分。 所以,当你订阅一个信号,该信号有所属参照用户和用户有一个所属的参考给你。 而你得到的回报一次性有所属参考信号。
disposable -> signal -> subscriber -> calling scope
假设你持有到一次性所以你可以在某些时候取消订阅(例如,如果信号从Web服务中检索数据和用户导航远离屏幕,取消了她的意图,查看被检索的数据)。
self.disposeToCancelWebRequest = disposable;
在这一点上,我们有一个循环引用:
calling scope -> disposable -> signal -> subscriber -> calling scope
该负责的事情是要确保取消的请求或请求完成后,当循环被打破。
[self.disposeToCancelWebRequest dispose]
self.disposeToCancelWebRequest = nil;
需要注意的是,你不能做到这一点,当self
被释放,因为这永远不会发生因保留周期! 事情似乎也有鬼回调到用户过程中打破保留周期,由于信号可能会被释放,而它的实现仍然在调用堆栈。
我还注意到,实施积极的保持信号的过程中,全局列表(如时间,我本来问这个问题)。
我应该如何看待所有权使用RAC的时候?
ReactiveCocoa的内存管理是相当复杂的,是诚实的,但值得最终的结果是, 你不需要为了处理他们保留的信号 。
如果您需要保留每一个信号框架,它会更加笨重的使用,尤其是对于那些使用期货等(例如,网络请求)单次信号。 你必须任何长期的信号保存到一个属性,然后还要确保清除出来,当你用它做。 不好玩。
认购
在进一步讨论之前,我要指出的是subscribeNext:error:completed:
(及其所有变体)创建使用给定的块一个隐含的用户。 从那些块中引用的任何对象都将因此被保留作为订阅的一部分。 就像任何其他的对象, self
不会没有直接或间接引用它保留。
(根据你的问题的措辞,我想你已经知道这一点,但也可能是其他人的帮助。)
有限的或短暂的信号
到RAC内存管理中最重要的准则是, 订阅完成或错误时自动终止,且用户删除 。 若要使用循环引用例如:
calling scope -> disposable -> signal -> subscriber -> calling scope
......这意味着signal -> subscriber
关系尽快拆除signal
完成,打破了保留周期。
这往往是你所需要的 ,因为寿命RACSignal
在内存中,自然会匹配事件流的逻辑寿命。
无限信号
(住这么久,他们很可能会成为无限或信号)无限的信号,但是,绝不会自然地推倒。 这是一次性的光芒。
订阅的处置将删除相关用户 ,只是一般清理与该预订关联的任何资源。 为了一个用户,它就像信号已经完成或出错,除了在信号被送到没有最终事件。 所有其他用户将保持不变。
然而,作为一个一般的经验法则, 如果你必须手动管理订阅的生命周期,可能有更好的方式做你想做的。 类似的方法-take:
或-takeUntil:
将处理处置你,你结束了一个更高层次的抽象。
从得到的信号self
目前仍然是一个有点棘手的中间情况下在这里,虽然。 任何时候,一个信号的生命周期联系在一起的呼叫范围,你就会有一个更难周期打破。
此使用时通常发生RACAble()
或RACAbleWithStart()
这是相对于一个关键路径上的self
,然后应用需要捕获块self
。
这里的最简单的答案就是捕捉self
弱 :
__weak id weakSelf = self;
[RACAble(self.username) subscribeNext:^(NSString *username) {
id strongSelf = weakSelf;
[strongSelf validateUsername];
}];
或者,导入包括后EXTScope.h头:
@weakify(self);
[RACAble(self.username) subscribeNext:^(NSString *username) {
@strongify(self);
[self validateUsername];
}];
(更换__weak
或@weakify
与__unsafe_unretained
或@unsafeify
,分别,如果对象不支持弱引用。)
然而, 有可能是一个更好的模式,你可以使用来代替。 例如,上面的示例,也许可以这样写:
[self rac_liftSelector:@selector(validateUsername:)
withObjects:RACAble(self.username)];
要么:
RACSignal *validated = [RACAble(self.username) map:^(NSString *username) {
// Put validation logic here.
return @YES;
}];
与无限信号,通常有可避免引用方式self
在信号链从块(或任何对象)。
以上信息真的那么你应该需要为了有效地使用ReactiveCocoa。 不过,我想解决一个多点,只是技术上的好奇或有兴趣的人奉献RAC:
我还注意到,实施积极的保持信号的过程中,全局列表。
这是千真万确的。
“没有必要保留”的设计目标,引出了一个问题:我们如何知道什么时候一个信号应该被释放? 如果它刚刚创建,躲过了一个自动释放池,并没有得到保留了吗?
真正的答案是我们不这样做 ,但我们通常可以假设主叫方将保留当前的运行循环迭代内的信号,如果他们想继续使用它。
所以:
- A创建信号被自动添加到全局组活动信号。
- 该信号将等待主运行循环的单传,然后从活动集中删除自身,如果它没有用户 。 除非信号莫名其妙地被保留,这将解除分配在这一点上。
- 如果事情没有在运行循环迭代订阅,信号保持在设定的。
- 后来,当所有用户都走了,#2再次被触发。
如果运行循环(在OS X中的情态事件循环等)递归纺这可能会适得其反,但它使框架消费者的生活大部分或所有其他情况下容易得多。
我试图解决的ReactiveCocoa 2.5内存管理谜
RACSubject* subject = [RACSubject subject];
RACSignal* signal = [RACSignal return:@(1)];
NSLog(@"Retain count of RACSubject %ld", CFGetRetainCount((__bridge CFTypeRef)subject));
NSLog(@"Retain count of RACSignal %ld", CFGetRetainCount((__bridge CFTypeRef)signal));
第一行输出1
,和第二线输出2
。 似乎RACSignal
将被保留的地方,而RACSubject
不是。 如果你没有明确保留RACSubject
,当程序退出当前的范围将被释放。