【问题标题】:ReactiveCocoa: creating a signal that applies a map over an observerReactiveCocoa:创建一个在观察者上应用地图的信号
【发布时间】:2016-05-19 12:45:45
【问题描述】:

据我了解,RACSubject 等效于 ReactiveCocoa 4 是 Observer 类。 我希望将信号和观察者链接在一起,以便信号发送事件,将映射操作应用于发送给观察者的事件。 在 Obj-C 中它看起来像这样:

// ViewModel.h

@interface ViewModel

@property (readonly) RACSubject *someAction; //expects e.g. int values
@property (readonly) RACSignal *someSignal; //sends e.g. string values

@end

// ViewModel.m

//redeclaring the signal and action as readwrite

@implementation

- (id)init {
  _someAction = [RACSubject subject];
  _someSignal = [_someAction map:^id(NSNumber *index) {
     return "Some string based on index passed in";
  }];
}

@end

现在,当有人将值推送到 someAction 时,someSignal 将触发包含派生值的事件。 如何在 Swift 中实现同样的效果?

到目前为止,我能做的事情是这样的:

public class ViewModel: NSObject {
    public let (internalSignal, someAction) = Signal<Int, NoError>.pipe()
    public var someSignal: Signal<String, NoError> {
        get {
            return self.internalSignal.map({ [unowned self](index: Int) -> String in
                return "Some string value based on \(self.someArray[index])"
            })
        }
    }
    public let someArray = [1, 2, 3, 4, 5]
}

这看起来是个糟糕的解决方案,因为

  1. internalSignal 应该是私有的,但需要声明为公共的,以便与 Signal 的管道匹配
  2. 因此,someSignal 是在每次需要时计算的,即使相同的信号可以一遍又一遍地重复使用。也不能声明为let 常量。

【问题讨论】:

    标签: objective-c swift reactive-cocoa reactive-cocoa-4


    【解决方案1】:

    您可以像 ObjC 一样初始化 init 中的成员...

    public class ViewModel: NSObject {
        private let internalSignal: Signal<Int, NoError>
        public let someAction: Observer<Int, NoError>
        public let someSignal: Signal<String, NoError>
    
        override init() {
            (internalSignal, someAction) = Signal<Int, NoError>.pipe()
            someSignal = internalSignal.map { index in
                "Some string value based on \(index)"
            }
            super.init()
        }
    }
    

    对于someSignal,您也可以使用lazy initialization,它允许会员引用self

    public class ViewModel: NSObject {
        private let internalSignal: Signal<Int, NoError>
        public let someAction: Observer<Int, NoError>
        public private(set) lazy var someSignal: Signal<String, NoError> =
            self.internalSignal.map { [unowned self] index in
                "Some string value based on \(self.someArray[index])"
            }
    
        override init() {
            (internalSignal, someAction) = Signal<Int, NoError>.pipe()
            super.init()
        }
    }
    

    与第一段代码不同,lazy-var 仅在使用 someSignal 之前初始化,而不是在 ViewModel 初始化时。

    另外,因为它是一个var,Swift 允许你改变它的值(没有lazy let 这样的东西)。我们可以使用private(set) 限制权限,但这不会阻止您在某处不小心写self.someSignal = ...


    或者,您可以将 someSignal 设为隐式展开的可选选项并手动初始化:

    public class ViewModel: NSObject {
        private let internalSignal: Signal<Int, NoError>
        public let someAction: Observer<Int, NoError>
        public private(set) var someSignal: Signal<String, NoError>!
    
        override init() {
            (internalSignal, someAction) = Signal<Int, NoError>.pipe()
            super.init()
            someSignal = internalSignal.map { [unowned self] index in
                "Some string value based on \(self.someArray[index])"
            }
        }
    }
    

    【讨论】:

    • 感谢您的快速回答。唯一的问题是在地图闭包中self 不能使用,因为我们仍处于初始化状态。让我重新表述一下这个问题。
    • 虽然我不太喜欢编写如此简单的想法所需的额外代码量,但我仍然感谢您的帮助。回答接受。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 1970-01-01
    • 2018-05-09
    • 2022-02-09
    相关资源
    最近更新 更多