ReactiveCocoa example with NSMutableArray push/pop

2019-03-09 11:16发布

问题:

Could someone provide a one-line example of using ReactiveCocoa abstractions to achieve something like this:

// pseudo-code
NSMutableArray *array = @[[] mutableCopy];
RACSignal *newValue = RACAbleWithStart(array); // get whole array or maybe just added/removed element on push/pop

[newValue subscribeNext:^(NSArray *x) {
  // x is whole array
}]

[newValue subscribeNext:^(id x) {
  // x is new value
}]

[newValue subscribeNext:^(id x) {
  // x is removed value
}]

I see that some extensions for NSArray were removed in favor of Mantle https://github.com/ReactiveCocoa/ReactiveCocoa/pull/130 But still can't find simple example of NSArray manipulation.

回答1:

You can't observe an array for changes. ReactiveCocoa uses key-value observation. Which, as the name suggests, only observes changes to keyed attributes (dictionary members, properties, etc.).

What you can do is observe an array property for changes:

@interface Blah : NSObject
@property (copy, readonly) NSArray *arrayProperty;
@end

// later...
Blah *blah = [Blah new];
[RACObserve(blah, arrayProperty) subscribeNext:^(NSArray *wholeArray){}];

If you want to know which objects where inserted/removed then you have two options. You could work it out by storing each array and comparing each with the previous. This is simplest but will perform badly with very large arrays. AFAIK, ReactiveCocoa does not have built-in operations to do this.

Or you could implement KVO collection accessors and ensure that changes to the array are made using mutableArrayValueForKey:. This avoids creating a new array whenever any changes are made, and also notifies observers of changes made to the proxy array returned by mutableArrayValueForKey:.

Observing change info with ReactiveCocoa is slightly more involved:

RACSignal *changeSignal = [blah rac_valuesAndChangesForKeyPath:@keypath(blah, arrayProperty) options: NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld observer:nil];
[changeSignal subscribeNext:^(RACTuple *x){
    NSArray *wholeArray = x.first;
    NSDictionary *changeDictionary = x.second;
}];

The change dictionary tells you what kind of change was made to the array, which objects were inserted/removed, and the indexes of the inserted/removed objects.

It is documented at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html



回答2:

The swift equivalent for Chris' solution:

let signal = self.object.rac_valuesAndChangesForKeyPath("property", options:      NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, observer:self.object)
signal.subscribeNext { (object) -> Void in
   if let tuple = object as? RACTuple {
        var wholeArray = tuple.first as? NSArray
        var changeDictionary = tuple.second as? NSDictionary
    }
}

Also make sure you change your contents property in KVO compliant ways.

// This is wrong and wont send values to RAC signals
[self.contents addObject:object];

// This is correct and will send values to RAC signals
NSMutableArray *contents = [account mutableArrayValueForKey:@keypath(self, contents)];
[contents addObject:object];

Edit: To make things more clear, put the name of your array in place of property. For example:

lazy var widgets:NSMutableArray = NSMutableArray()
let signal = self.rac_valuesAndChangesForKeyPath("widgets", options:      NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, observer:self)