![](https://img.haomeiwen.com/i1477238/910f41dec42677ff.jpeg)
这篇文章来总结下Property和MutableProperty。
Property
属性是一个可观察的容器,每当它的值被更改时,它就会发出它的值。它遵守PropertyProtocol协议,本身具有以下属性:
- value: 表示当前值。
- producer: 一个发送处在变化中的当前值的SignalProducer。
- signal: 发送连续变化的信号,不发送当前值。
![](https://img.haomeiwen.com/i1477238/660f2bd374c1fa78.png)
它有什么用呢?
当我们只需要处理值而不是错误时,可以用Property。还是举个例子吧:
每隔5秒打印一条消息。
第一步,创建signalProducer:
let signalProducer: SignalProducer<Int, NoError> = SignalProducer { (observer, lifetime) in
let now = DispatchTime.now()
for index in 0..<10 {
let timeElapsed = index * 5
DispatchQueue.main.asyncAfter(deadline: now + Double(timeElapsed))
{
guard !lifetime.hasEnded else {
observer.sendInterrupted()
return
}
observer.send(value: timeElapsed)
if index == 9 {
observer.sendCompleted()
}
}
}
}
Note:这里的错误类型时NoError,我们不用处理错误,可以很好地用Property来封装。
第二步,创建Property:
let property = Property(initial: 0, then: signalProducer)
这里,我们给了初始值0,上面讲过Property包含一个signal和producer,signal不发送当前值,仅仅发送值的连续变化。
第三步,使用property的producer启动执行:
property.producer.startWithValues { value in
print("[Observing SignalProducer] Time elapsed = \(value)")
}
最终输出:
[Observing SignalProducer] Time elapsed = 0
[Observing SignalProducer] Time elapsed = 0
[Observing SignalProducer] Time elapsed = 5
[Observing SignalProducer] Time elapsed = 10
[Observing SignalProducer] Time elapsed = 15
[Observing SignalProducer] Time elapsed = 20
[Observing SignalProducer] Time elapsed = 25
[Observing SignalProducer] Time elapsed = 30
[Observing SignalProducer] Time elapsed = 35
[Observing SignalProducer] Time elapsed = 40
[Observing SignalProducer] Time elapsed = 45
第四步,使用property的signal启动执行:
property.signal.observeValues { value in
print("[Observing Signal] Time elapsed = \(value)")
}
最终输出:
[Observing Signal] Time elapsed = 0
[Observing Signal] Time elapsed = 5
[Observing Signal] Time elapsed = 10
[Observing Signal] Time elapsed = 15
[Observing Signal] Time elapsed = 20
[Observing Signal] Time elapsed = 25
[Observing Signal] Time elapsed = 30
[Observing Signal] Time elapsed = 35
[Observing Signal] Time elapsed = 40
[Observing Signal] Time elapsed = 45
上面这个例子,signal没有把初始值打印出来,我们创建了一个通过SignalProducer来获取值的Property,其实也可以创建一个通过Signal来获取值的property。
MutableProperty
MutableProperty是一个可观察的容器,它像属性一样在更改时发出值,但也可以直接修改值。和Property一样,它也遵循propertyprotocol协议。
举个例子吧。
第一步,给个初始值创建mutableProperty:
let mutableProperty = MutableProperty(1)
第二步,修改value:
mutableProperty.value = 3
第三步,绑定
mutableProperty <~ signalProducer
Property/MutableProperty发送事件,从冷热信号的定义上来看, Property的行为应该属于热信号, 但和上文的Signal不同, Property/MutableProperty只提供一种状态的事件: Value(虽然它有Completed状态)。
Property/MutableProperty对比使用
首先,看Property:
let constant = Property(value: 1)
// constant.value = 2 // error: Property(value)创建的value不可变
constant.producer.startWithValues { (value) in
print("producer received: (value)")
}
//并不会被触发,所以不会打印
constant.signal.observeValues { (value) in
print("signal received: (value)")
}
最终输出:
producer received: 1
可以看出,signal并没有被触发,这个和上面的例子不同之处在于:上面的Property跟一个SignalProducer绑定,接管了值的变化,所以上面的例子中的signal可以被触发。
其次,看MutableProperty:
let mutableProperty = MutableProperty(1)
//冷信号可以收到初始值value=1和2,3
mutableProperty.producer.startWithValues {
print("producer received ($0)")
}
//冷信号可以收到初始值value=1和2,3
mutableProperty.signal.observeValues {
print("signal received ($0)")
}
mutableProperty.value = 2
mutableProperty.value = 3
最终输出:
producer received: 1
producer received: 2
signal received: 2
producer received: 3
signal received: 3
可以看出,由于producer是冷信号,前后的事件都能接收到,而signal是热信号,只能接收到后来的事件,而且Property值的每次更改设置就是在发送Value事件。
综上所述:
- Property.value不可设置,MutableProperty.value可设置。
- Property/MutableProperty内部有一个Producer和一个Signal,设置value即是在向这两个信号发送Value事件。
最后,来个实例展示
再来看个具体的手机号输入限制的实例,要求是:
- 用户输入手机号,限制手机号最多输入11个数字。
- 验证手机号是否有效,手机号无效需要展示错误信息。
let errorLabel: UILabel
let sendButton: UIButton
let phoneNumerTextField: UITextField
var errorText = MutableProperty("")
var validPhoneNumer = MutableProperty("")
//进行一系列绑定
errorLabel.reactive.text <~ errorText
sendButton.reactive.isEnabled <~ errorText.map{ $0.count == 0 }
sendButton.reactive.backgroundColor <~ errorText.map{ $0.count == 0 ? UIColor.red : UIColor.gray }
phoneNumerTextField.reactive.text <~ validPhoneNumer
//有效输入的数据来自输入框输入变化
validPhoneNumer <~ phoneNumerTextField.reactive.continuousTextValues
.map({(text) -> String in
let phoneNumer = (text ?? "").substring(to: 11)
//检查手机格式是否正确
let isValidPhoneNum = NSPredicate(format: "SELF MATCHES %@", "^1(3[0-9]|5[0-35-9]|8[025-9])\d{8}$").evaluate(with: phoneNumer)
errorText.value = isValidPhoneNum ? "手机号格式不正确" : ""
return phoneNumer
})
网友评论