NSNotificationCenter与ReactiveCocoa 3和Swift的正确使用是什么?

Gui*_*oMB 7 nsnotificationcenter ios reactive-cocoa swift

我正在玩ReactiveCocoa 3和Swift,我决定做一个简单的玩具应用程序来测试如何使用实现MVVM模式的ReactiveCocoa 3设计应用程序.

基本的工作人员工作得很好,但我不确定处理通知中心创建的信号的最佳方法是什么.

让我们说有人在应用程序的某个地方触发通知.通知被命名,TimerNotification并且Int在密钥可访问的用户信息字典内具有时间对象TimerCount.现在假设我有一个控制器,想要在每次TimerNotification触发时打印一条消息.

在旧的ObjC/RAC 2天里,我会做这样的事情

- (void)viewDidLoad {
  NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
  RACSignal * timerSignal = [[notificationCenter rac_addObserverForNotificationName:@"TimerNotification" object:nil] 
    takeUntil:self.rac_willDeallocSignal];
  [timerSignal subscribeNext:^(NSNotification * notification){
    NSValue * timerCount = notification.userInfo[@"TimerCount"];
    NSLog(@"Timer count is %@", timerCount);
  }];
}
Run Code Online (Sandbox Code Playgroud)

这将确保当控制器被解除分配时,将处理订阅.

我第一次尝试在Swift/RAC 3世界中做类似的事情是

private func createTimerSignalProducer() -> SignalProducer<Int, NoError> {
  let notificationCenter = NSNotificationCenter.defaultCenter()
  let deallocSignalProducer = self.rac_willDeallocSignal().toSignalProducer()
      |> map { _ in () }
      |> catch { _ in SignalProducer.empty as SignalProducer<(), NoError> }

  return notificationCenter.rac_notifications(name: "TimerNotification")
      |> map { $0.userInfo!["TimerCount"] as! Int }
      |> takeUntil(deallocSignalProducer)
}
Run Code Online (Sandbox Code Playgroud)

然后在里面viewDidLoad我会这样做

createTimerSignalProducer()
    |> start { count in
        println("Timer trigger for the \(count) time")
    }
Run Code Online (Sandbox Code Playgroud)

这实际上有效但如果你想在一个不从NSObject继承的对象中做类似的事情.因为在常规的Swift对象中你没有得到rac_willDeallocSignal().

一种可能的解决方案是将一次性物品存放在实例变量中,然后将其丢弃,deinit但我希望避免手动处理一次性物品.

UPDATE

我最终做了什么(因为Swift对象没有根对象)是

public protocol ViewModel {

}

public class BaseViewModel: ViewModel {

    private let deinitSignalProducerSinkPair = SignalProducer<(), NoError>.buffer()

    public var deinitSingalProducer: SignalProducer<(), NoError> {
        return deinitSignalProducerSinkPair.0
    }

    deinit {
        sendNext(deinitSignalProducerSinkPair.1, ())
    }

}
Run Code Online (Sandbox Code Playgroud)

然后在我的视图模型中

public class DetailViewModel: BaseViewModel {

    let title: String
    let subtitle: String
    let author: String
    let createdAt: NSDate
    let timerCounter = MutableProperty<Int>(0)
    let inputText = MutableProperty<String>("")
    let reverseInputText = MutableProperty<String>("")

    var formattedCreatedAt: String {
        let formatter = NSDateFormatter()
        formatter.dateFormat = "dd/MM/yy"
        return formatter.stringFromDate(createdAt)
    }

    public required init(title: String, subtitle: String, author: String, createdAt: NSDate) {
        self.title = title
        self.subtitle = subtitle
        self.author = author
        self.createdAt = createdAt
        super.init()

        timerCounter <~ createTimerSignalProducer()
        reverseInputText <~ (inputText.producer |> map { String(reverse($0)) })
    }

    // MARK - Private methods

    private func createTimerSignalProducer() -> SignalProducer<Int, NoError> {
        return NSNotificationCenter.defaultCenter().rac_notifications(name: "TimerNotification")
            |> map { $0.userInfo!["TimerCount"] as! Int }
            |> takeUntil(deinitSingalProducer)
    }
}
Run Code Online (Sandbox Code Playgroud)