2014-05-13 29 views
1

下我有一個RACSignal攜帶的NSArray類對象的書籤添加並從RACSignal

RACSignal *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財產相比),但我無法弄清楚如何在純粹的方式做到這一點。

回答

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]; 
+0

不錯。將命令映射到只執行轉換的塊而不是要執行的轉換的描述會更清晰。 (斜線,你明白'bookmarkmarksSignal'已經存在,我錯過了) –

+0

謝謝:)不同的事件類型讓我思考函數的信號。 –

+0

我真的很喜歡函數信號的想法。這是我去的解決方案,但有一些變化。由於特殊原因,我需要通過純功能的代碼,所以我將書籤信號映射到類型* NSArray - > NSArray *(使用'arrayByAddingObject:'和過濾)的函數,然後使用'scanWithStart:reduce:'組合函數。 –

1

讓我們暫時忘掉RACCommand一秒鐘,然後假裝我們只有一個要添加書籤的信號和一個要刪除的書籤信號,並且我們希望基於此創建一組書籤信號。這是一個很好的起點,我們可以想出如何在事後實現這些。

RACSignal *addedSignal = ...; 
RACSignal *removedSignal = ...; 

然後,我們希望這是這兩個信號組合成一組的所有添加的東西沒有任何事後刪除(我們可以在事後將其映射到一個NSArray如果我們想)的信號。

RACSignal *bookmarkSetSignal = ...; 

現在我們必須填充它。我們可以通過使我們修改,只是發送引用到同一組的每一個變化發生時一個可變集優化這裏。但這與信號的性質有些相反。讓我們暫時擱置一下這個優化,然後做一個純粹的,功能性的方法。

我們打算使用scanWithStart:reduce:方法,因爲它完全適合這個問題。這就像是一個返回每個中間值的fold,這正是我們想要的。

但首先我們必須使addedSignalremovedSignal有用。這是我的想法:將它們合併成一個單一的信號,但附加另一個值,說明它是一個添加還是刪除。

// 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是應該發生的變化(書籤+是否被添加或刪除)的描述。我們現在有一組書籤的信號,就像我們想要的一樣!

除現在我們需要填寫addedSignalremovedSignal

我們這樣做的確切方式有點...好吧,這取決於用戶的交互。我們可以讓每個人都成爲一個主題,然後用戶交互將手動發送新值。這可能是正確的做法。它比手動觸發RACCommand更清潔。無論如何,這是一個單獨的問題。假設你現在有確切RACCommand格式,我認爲我們可以實現它是這樣的:

RACSignal *addedSignal = [self.addBookmarkCommand.executionSignals switchToLatest]; 
RACSignal *removedSignal = [self.deleteBookmarkCommand.executionSignals switchToLatest]; 

executionSignals是在信號塊返回的每個信號的信號。其中只有[RACSignal return:bookmark]。通過switch ing ToLatest,我們基本上「解開」了這個價值。但是如果我們使用了一個主題,我們不需要首先打包/解包。但無論如何,單獨的討論。

這段代碼幾乎肯定需要一點修改才能做你想做的事(沒有經過測試),但希望這會爲你指出明智的正確方向。