ReactiveCocoa的引用所有权语义是什么?
当我创建一个信号并将其带入函数的范围时,其有效保留计数为0每Cocoa约定:
RACSignal *signal = [self createSignal]; 当我订阅信号时,它保留订阅者并返回一个一次性的,根据Cocoa约定,保留计数为零。 RACDisposable *disposable = [signal subscribeCompleted:^ { doSomethingPossiblyInvolving(self); }]; 大多数时候,订阅者将关闭并引用自身或其ivars或包围范围的其他部分。因此,当您订阅信号时,信号具有对订户的拥有引用,并且订户具有对您的拥有引用。而一次性你得到的回报有一个拥有参考的信号。 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 …这意味着信号 – 一旦信号完成,用户关系就立即断开,打破保持周期。 这通常是你需要的,因为RACSignal在内存中的生命周期自然会匹配事件流的逻辑生命周期。 无限信号 无限信号(或信号,生活得如此长,以至于它们可能是无限的),然而,永远不会自然地撕裂。这是一次性用品。 处理订阅将删除相关联的订户,并且通常只清除与该订阅相关联的任何资源。对于那个用户,它就像信号已经完成或错误,除了没有最终事件发送信号。所有其他订阅者将保持不变。 然而,作为一般的经验法则,如果你必须手动管理订阅的生命周期,可能有更好的方法来做你想要的。像-take:或-takeUntil:这样的方法会处理你的处理,你最终得到一个更高级的抽象。 信号来源于自我 这里仍然有一个棘手的中间情况。任何时候一个信号的生命周期都绑定到调用范围,你将有一个更难的周期来打破。 当在相对于self的密钥路径上使用RACAble()或RACAbleWithStart(),然后应用需要捕获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; }]; 与无限信号一样,通常有一些方法可以避免引用来自信号链中块的自身(或任何对象)。 上面的信息真的是所有你需要为了有效地使用ReactiveCocoa。但是,我想再谈一点,只是为了技术上的好奇或任何有兴趣参与RAC的人:
这是绝对真实的。 “不需要保留”的设计目标提出了一个问题:我们怎么知道什么时候信号应该被释放?如果它刚刚创建,转义自动释放池,并且尚未保留,该怎么办? 真正的答案是我们不,但是我们可以通常假设调用者将保留当前运行循环迭代中的信号,如果他们想保留它。 所以: >创建的信号自动添加到全局活动信号集。 如果运行循环被递归地旋转(如在OS X上的模态事件循环),这可能会回火,但是它使得框架消费者的生活对于大多数或所有其他情况更容易。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ruby-on-rails – 默认值:Rails资源路由的排除选
- c – 如何正确使用std:vector的erase()函数?
- xml – 什么是Unicode U 001A字符?又名0x1A
- 第一次自己写存储过程去进行设备录入――存做纪念
- Flex样式-ApplicationControlBar篇
- c# – 如何将CancellationTokenSource附加到Down
- 5.9 类析构方法(deinit)的使用 [Swift原创教程]
- 未能加载文件或程序集或它的某一个依赖项
- objective-c – 具有多个CDVViewController的Pho
- c# – 哪种类库适用于Universal和Winform项目?