我有一个RACSignal,它携带了Bookmark
类对象的NSArrayRACSignal *bookmarksSignal = ... // Carries @[[[Bookmark alloc] init], ...]
两个RACCommands添加和删除书签
RACCommand *addBookmarkCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input){
return [RACSignal return:input];
}];
...
[addBookmarkCommand execute: bookmark1];
RACCommand *deleteBookmarkCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input){
return [RACSignal return:input];
}];
...
[deleteBookmarkCommand execute: bookmark2]
在用户交互上执行命令。如何组合bookmarksSignal
和两个命令的执行信号来创建带有书签的NSArray的信号,该书签包含addBookmarkCommand
添加的原始书签而没有deleteBookmarkCommand
删除的书签?
(书签按url
属性进行比较)
我担心我会遗漏一些明显的东西,但我无法弄清楚如何以纯粹的方式做到这一点。
答案 0 :(得分:2)
由于时间不够,与@ IanHenry相比,我会选择更简洁的答案:)
对于任何没有意义的内容,请发表评论,我会很乐意详细解释。
// Map additions into blocks that add the bookmark to the given mutable array.
RACSignal *addBookmarkSignal = [[addBookmarkCommand concat] map:^(Bookmark *bookmark) {
return ^(NSMutableArray *bookmarks) {
return [bookmarks addObject:bookmark];
};
}];
// Map deletions into blocks that remove the bookmark from the given mutable array.
RACSignal * addBookmarkSignal = [[deleteBookmarkCommand concat] map:^(Bookmark *bookmark) {
return ^(NSMutableArray *bookmarks) {
[bookmarks removeObject:bookmark];
};
}];
// Combine the add and delete functions into a single signal.
RACSignal *updatesSignal = [RACSignal merge:@[addBookmarkSignal, addBookmarkSignal]];
RACSignal *updatedBookmarksSignal = [[bookmarksSignal
// Each time bookmarksSignal sends an array, this -map: builds a
// signal that updates the latest list of bookmarks, and sends it.
map:^(NSArray *bookmarks) {
NSMutableArray *mutableBookmarks = [bookmarks mutableCopy];
// Using the update blocks from the add/delete commands,
// produce the modified list of bookmarks.
return [[updatesSignal
map:^(void (^update)(NSMutableArray *)) {
update(mutableBookmarks);
return [mutableBookmarks copy];
}]
startWith:bookmarks];
}]
// When bookmarksSignal sends anew, switch to the newest signal of updates.
switchToLatest];
答案 1 :(得分:1)
让我们暂时忘记RACCommand
,并假装我们只是要添加书签的信号和要删除的书签信号,我们想要根据这些信息创建一组书签信号。这是一个很好的起点,我们可以弄清楚如何在事后实现这些。
RACSignal *addedSignal = ...;
RACSignal *removedSignal = ...;
然后我们想要一个信号,即将这两个信号组合成一组所有添加的东西,之后没有任何东西被移除(如果我们想要,我们可以将它映射到NSArray
。) / p>
RACSignal *bookmarkSetSignal = ...;
现在我们必须填写它。我们可以通过创建我们修改的可变集来优化这里,并且每次发生更改时只发送对同一集的引用。但这与信号的本质相反。让我们把这个优化保持一分钟,然后以纯粹的,功能性的方式进行。
我们将使用scanWithStart:reduce:
方法,因为它完全适合这个问题。它就像一个fold
,返回每个中间值,这正是我们想要的。
但首先我们必须使addedSignal
和removedSignal
有用。这是我的想法:将它们合并为一个信号,但是将另一个值附加到它们上,表示它是添加还是删除。
// turn a signal of bookmarks into a signal of tuples of the form (bookmark, isAdded)
RACSignal *changes = [RACSignal merge:@[[addedSignal map:^(id x) { return RACTuplePack(x, @YES); }],
[removedSignal map:^(id x) { return RACTuplePack(x, @NO); }]]];
现在它只是一个信号,它会更容易纠结它。然后我们可以将这些更改折叠成单个值(所有更改的组合),除了我们报告的每一步。所以这是扫描,而不是折叠。但扫描是一个不好的动词。无论如何:
RACSignal *bookmarkSetSignal = [changes scanWithStart:[NSSet set] reduce:^(NSSet *running, RACTuple *next) {
RACTupleUnpack(Bookmark *bookmark, NSNumber *isAddingNumber) = next;
if (isAddingNumber.boolValue) {
[running setByAddingObject:bookmark];
} else {
// you can do this with a much nicer helper, but this is the shortest way for this answer...
return [running filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"url != %@", bookmark.url]];
}
}];
大!我们从一个空集开始,每次发生更改时,我们都会通过添加或删除该元素来创建一个新集。 running
始终是我们上次计算的任何内容(从空集开始),next
是应该发生的更改的描述(书签+无论是否添加或删除)。我们现在有一组书签的信号,就像我们想要的那样!
现在我们需要填写addedSignal
和removedSignal
。
我们这样做的确切方式有点......好吧,这取决于用户的互动。我们可以将每个人作为主题,然后用户交互将手动发送新值。这可能是正确的方法。它比手动触发RACCommand
更清晰。无论如何,这是一个单独的问题。假设您现在拥有完全相同的RACCommand格式,我认为我们可以像这样实现它:
RACSignal *addedSignal = [self.addBookmarkCommand.executionSignals switchToLatest];
RACSignal *removedSignal = [self.deleteBookmarkCommand.executionSignals switchToLatest];
executionSignals
是信号块中返回的每个信号的信号。这仅仅是[RACSignal return:bookmark]
。通过switch
ToLatest
,我们基本上“解开”了这个价值。但是如果我们使用了一个主题,我们就不需要首先进行包装/解包。但无论如何,单独讨论。
这段代码几乎肯定需要做一些修改才能做你想做的事情(没有经过测试),但希望这会让你指出正确的方向。