ios - RACSignal 和一系列 View 模型,有没有更被动的方式?

标签 ios objective-c mvvm reactive-cocoa

我使用UITableViewController来显示发现的设备。它们是从 SDDeviceBrowser 获取的,SDDeviceBrowser 会不断扫描它们,并在每次发现新设备时调用其委托(delegate)方法。我创建了一个RACSignal,如下所示:

@implementation SDDeviceBrowser (RAC)
- (RACSignal*)rac_newDeviceSignal {
    return [self rac_signalForSelector:@selector(deviceBrowserDidFindNewDevice:) fromProtocol:@protocol(SDDeviceBrowserDelegate)];
}
@end

我使用 MVVM 模式:我的 TableView 的 View 模型是 DeviceListViewModel。它有一个数组devices,其中包含绑定(bind)到 TableView 单元格的 subview 模型。它映射浏览器的信号并将其公开给 View Controller :

@interface DeviceListViewModel ()
@property (strong, readwrite, nonatomic) NSArray *devices;
@property (strong, nonatomic) SDDeviceBrowser *browser;
@end

//boring initialization ommitted

- (RACSignal *)deviceFoundSignal {
    return [[self.browser rac_newDeviceSignal] map:^id(RACTuple* parameters) {
      SDDevice *device = parameters.last;
      DeviceViewModel *deviceViewModel = [[DeviceViewModel alloc] initWithDevice:device];
      self.devices = [self.devices arrayByAddingObject:deviceViewModel];
      return deviceViewModel;
    }];
}

然后, TableView Controller 订阅 deviceFoundSignal 并在发现新设备时插入一行:

[[self.viewModel.deviceFoundSignal throttle:0.5] subscribeNext:^(id value) {
  [self.refreshControl endRefreshing];
  //insert new rows to the table view inside beginUpdates/endUpdates 
}];

还可以“重置”设备浏览器:它会清除已发现设备的列表并重新开始扫描。但是,我找不到一个很好的响应式解决方案来处理这个问题 - 我只是执行以下操作(在 View Controller 中):

[[self.refreshControl rac_signalForControlEvents:UIControlEventValueChanged] subscribeNext:^(id x) {
  [self.viewModel restartScanning];  //clears the 'devices' array and restarts the browser
  [self.tableView reloadData];
}];

这可行,但我认为可以通过更“响应式(Reactive)”的方式来完成。将新 View 模型添加到 map: block 内的数组看起来有点难看。我是否遗漏了可以在此处使用的 ReactiveCocoa 的任何功能?

最佳答案

要组装设备阵列,请查看 -scanWithStart:reduce: 。使用此方法,您可以从一个空数组开始,然后让reduce block 将每个设备添加到数组中。例如:

[[[self.browser
    rac_newDeviceSignal]
    map:^(RACTuple *parameters) {
        SDDevice *device = parameters.last;
        return [[DeviceViewModel alloc] initWithDevice:device];
    }]
    scanWithStart:@[] reduce:^(NSArray models, DeviceViewModel *deviceViewModel) {
        return [devices arrayByAddingObject:deviceViewModel];
    }]

这对于“重置”功能没有多大作用。要将“添加”和“重置”合并到一个信号和一次扫描中,我将执行以下操作:

首先,在代码中找到可以公开两个相关信号的位置,即 -rac_newDeviceSignal 以及哪个信号代表导致重置的事件。我将后一个信号称为“resetSignal”。

通过设备添加信号和重置信号,我会将它们分别映射到设备数组上的“操作”。我所说的“操作”是什么意思,基本上是一个获取旧设备数组并返回新设备数组的 block 。

RACSignal *addOperation = [[self.browser rac_newDeviceSignal] map:^(RACTuple *parameters) {
    return ^(NSArray *devices) {
        SDDevice *device = parameters.last;
        DeviceViewModel *model = [[DeviceViewModel alloc] initWithDevice:device];
        return [devices arrayByAddingObject:model];
    };
}]

RACSignal *resetOperation = [resetSignal map:^(id _) {
    return ^(NSArray *devices) {
        return @[];
    };
}]

有了这两个信号,就可以将它们+merge:合并为一个信号,然后可以像上面所示的那样对其进行扫描。

[[RACSignal
    merge:@[ addOperation, resetOperation ]]
    scanWithStart:@[] reduce:(NSArray *devices, NSArray *(^operation)(NSArray *)) {
        return operation(devices);
    }]

关于ios - RACSignal 和一系列 View 模型,有没有更被动的方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28306991/

相关文章:

objective-c - 无法弄清楚内存泄漏从何而来

ios - nil 合并运算符 '??' 的左侧具有非可选类型 'Int' ,因此右侧从 swift 1.2 转换为 4 后从未使用警告

ios - 无法在 iOS 中更改 UIWebView 的背景

ios - UITextPosition 到 NSRange

mvvm - Prism 7 - 将 IContainer 对象注入(inject) View 模型

c# - 重构ViewModels

.net - 为什么在 MVVM 中使用命令

ios - InAppSettingsKit - 能够选择多个值

objective-c - 我应该在哪里以及如何实例化一个将在 IOS 应用程序中全局使用的对象?

ios - 在 Spritekit 上将一个 ViewController 导航到另一个