观察 Swift 5 中变化的通用协议
Generic protocol for observing changes in Swift 5
努力编写 Swift5 下面的 Java 示例。
一般来说,我想要一个 Observable
协议,它将被多个其他协议采用。我需要将这些协议作为函数参数的类型,以便这些函数可以添加额外的观察者。
在Java中,非常容易做到。代码打印出来:
Observer 1 changed to 10
Observer 2 changed to 10
,
interface Observable<O> {
void addObserver(O observer);
}
interface Settings extends Observable<SettingsObserver> {
void setInterval(int interval);
}
interface SettingsObserver {
void intervalChanged(int interval);
}
class AppSettings implements Settings {
private List<SettingsObserver> observers = new ArrayList<>();
@Override public void addObserver(SettingsObserver observer) { observers.add(observer); }
@Override public void setInterval(int interval) { observers.forEach(observer -> observer.intervalChanged(interval)); }
}
class Observer1 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 1 changed to " + interval);
}
}
class Observer2 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 2 changed to " + interval);
}
}
class Main {
public static void main(String[] args) {
Observer1 observer1 = new Observer1();
Settings settings = new AppSettings();
settings.addObserver(observer1);
Main main = new Main();
main.run(settings);
}
void run(Settings settings) {
Observer2 observer2 = new Observer2();
settings.addObserver(observer2);
settings.setInterval(10);
}
}
根据您的需要,您可以在 Swift 中使用 property observers。它允许您在 属性 将要更改或已经更改时采取行动。它也没有完整的可观察类型复杂。
这是 Swift 手册中 Apple 的示例:
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
您可能需要使用 didSet() 函数。您还可以在观察者中调用另一个函数。
如果你不想使用 RxSwift 或 Apple 的新框架,你也可以使用 属性 观察者写一个简单的 observable-like class合并.
这是一个简单的例子,它只使用闭包而不是 classes:
class ClassToWatch {
typealias ObservingFunc = (ClassToWatch) -> Void
private var observers: [ObservingFunc] = []
func addObserver(_ closure: @escaping ObservingFunc) {
observers.append(closure)
}
private func valueChanged() {
observers.forEach { observer in
observer(self)
}
}
var value1: Int = 0 {
didSet {
valueChanged()
}
}
var value2: String = "" {
didSet {
valueChanged()
}
}
}
var myclass = ClassToWatch()
myclass.addObserver { object in
print("Observer 1: \(object.value1) \(object.value2)")
}
myclass.addObserver { object in
print("Observer 2: \(object.value1) \(object.value2)")
}
myclass.value1 = 3
myclass.value2 = "Test"
您的 Java 代码可以直接转换为 Swift 代码。这是我的翻译,有一定程度的 "Swiftification":
protocol Observable {
associatedtype ObserverType
func addObserver(_ observer: ObserverType)
}
protocol Settings : Observable where ObserverType == SettingsObserver {
var interval: Int { get set }
}
protocol SettingsObserver {
func intervalDidChange(newValue: Int)
}
class Observer1 : SettingsObserver {
func intervalDidChange(newValue: Int) {
print("Observer 1 changed to \(newValue)")
}
}
class Observer2 : SettingsObserver {
func intervalDidChange(newValue: Int) {
print("Observer 2 changed to \(newValue)")
}
}
class AppSettings: Settings {
var interval: Int = 0 {
didSet {
observers.forEach { [=10=].intervalDidChange(newValue: interval) }
}
}
private var observers: [SettingsObserver] = []
func addObserver(_ observer: SettingsObserver) {
observers.append(observer)
}
}
let settings = AppSettings()
settings.addObserver(Observer1())
settings.addObserver(Observer2())
settings.interval = 10
虽然 Observable
不能用作参数类型,但从它派生的协议也指定关联类型,可以。
您可以更进一步,使 SettingsObserver
成为 (Int) -> Void
的类型别名。这样你就不需要所有那些不同的 ObserverX
类.
typelias SettingsObserver = (Int) -> Void
addObserver
调用将变为:
settings.addObserver { print("Observer 1 changed to \([=12=])") }
settings.addObserver { print("Observer 2 changed to \([=12=])") }
并且 didSet
中的调用将更改为:
observers.forEach { [=13=](interval) }
另外,我不明白为什么会有Settings
。不能直接让 AppSettings
符合 Observable
吗?我的意思是,我知道程序接口的想法等等,但我觉得这有点过分了......
虽然创建一个您可以添加自己的可观察对象的通用包装器很简单,但您应该改用两个 native 解决方案。
通知。
值更改时,使用NotificationCenter.default
发送通知。观察员应收听这些通知。通知是生态系统的重要组成部分:
class AppSettings {
enum Notifications {
static let intervalChanged = Notification.Name("AppSettingsIntervalChangedNotification")
}
var interval: TimeInterval = 0 {
didSet {
NotificationCenter.default.post(name: Notifications.intervalChanged, object: self)
}
}
}
let settings = AppSettings()
let observer = NotificationCenter.default.addObserver(
forName: AppSettings.Notifications.intervalChanged,
object: settings,
queue: nil
) { [weak settings] _ in
guard let settings = settings else { return }
print(settings.interval)
}
settings.interval = 10
Key-value 观察 (KVO)
如果您从 NSObject
继承您的对象,您可以简单地将直接观察者添加到任何 Obj-C 兼容值:
class AppSettings: NSObject {
@objc dynamic var interval: TimeInterval = 0
}
let settings = AppSettings()
let observer: NSKeyValueObservation = settings.observe(\.interval, options: .new) { _, change in
print(change.newValue)
}
settings.interval = 10
为了完整起见,这里有一个简单的通用观察者:
class Observable<ValueType> {
typealias Observer = (ValueType) -> Void
var observers: [Observer] = []
var value: ValueType {
didSet {
for observer in observers {
observer(value)
}
}
}
init(_ defaultValue: ValueType) {
value = defaultValue
}
func addObserver(_ observer: @escaping Observer) {
observers.append(observer)
}
}
class AppSettings {
let interval: Observable<TimeInterval> = Observable(0)
}
let settings = AppSettings()
settings.interval.addObserver { interval in
print(interval)
}
settings.interval.value = 10
请注意,我所有的观察者都是简单的闭包。由于 Java 的限制,Java 使用对象作为观察者的原因主要是历史原因。 Swift.
中不需要 Observable
或 Observer
协议
努力编写 Swift5 下面的 Java 示例。
一般来说,我想要一个 Observable
协议,它将被多个其他协议采用。我需要将这些协议作为函数参数的类型,以便这些函数可以添加额外的观察者。
在Java中,非常容易做到。代码打印出来:
Observer 1 changed to 10
Observer 2 changed to 10
,
interface Observable<O> {
void addObserver(O observer);
}
interface Settings extends Observable<SettingsObserver> {
void setInterval(int interval);
}
interface SettingsObserver {
void intervalChanged(int interval);
}
class AppSettings implements Settings {
private List<SettingsObserver> observers = new ArrayList<>();
@Override public void addObserver(SettingsObserver observer) { observers.add(observer); }
@Override public void setInterval(int interval) { observers.forEach(observer -> observer.intervalChanged(interval)); }
}
class Observer1 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 1 changed to " + interval);
}
}
class Observer2 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 2 changed to " + interval);
}
}
class Main {
public static void main(String[] args) {
Observer1 observer1 = new Observer1();
Settings settings = new AppSettings();
settings.addObserver(observer1);
Main main = new Main();
main.run(settings);
}
void run(Settings settings) {
Observer2 observer2 = new Observer2();
settings.addObserver(observer2);
settings.setInterval(10);
}
}
根据您的需要,您可以在 Swift 中使用 property observers。它允许您在 属性 将要更改或已经更改时采取行动。它也没有完整的可观察类型复杂。
这是 Swift 手册中 Apple 的示例:
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
您可能需要使用 didSet() 函数。您还可以在观察者中调用另一个函数。
如果你不想使用 RxSwift 或 Apple 的新框架,你也可以使用 属性 观察者写一个简单的 observable-like class合并.
这是一个简单的例子,它只使用闭包而不是 classes:
class ClassToWatch {
typealias ObservingFunc = (ClassToWatch) -> Void
private var observers: [ObservingFunc] = []
func addObserver(_ closure: @escaping ObservingFunc) {
observers.append(closure)
}
private func valueChanged() {
observers.forEach { observer in
observer(self)
}
}
var value1: Int = 0 {
didSet {
valueChanged()
}
}
var value2: String = "" {
didSet {
valueChanged()
}
}
}
var myclass = ClassToWatch()
myclass.addObserver { object in
print("Observer 1: \(object.value1) \(object.value2)")
}
myclass.addObserver { object in
print("Observer 2: \(object.value1) \(object.value2)")
}
myclass.value1 = 3
myclass.value2 = "Test"
您的 Java 代码可以直接转换为 Swift 代码。这是我的翻译,有一定程度的 "Swiftification":
protocol Observable {
associatedtype ObserverType
func addObserver(_ observer: ObserverType)
}
protocol Settings : Observable where ObserverType == SettingsObserver {
var interval: Int { get set }
}
protocol SettingsObserver {
func intervalDidChange(newValue: Int)
}
class Observer1 : SettingsObserver {
func intervalDidChange(newValue: Int) {
print("Observer 1 changed to \(newValue)")
}
}
class Observer2 : SettingsObserver {
func intervalDidChange(newValue: Int) {
print("Observer 2 changed to \(newValue)")
}
}
class AppSettings: Settings {
var interval: Int = 0 {
didSet {
observers.forEach { [=10=].intervalDidChange(newValue: interval) }
}
}
private var observers: [SettingsObserver] = []
func addObserver(_ observer: SettingsObserver) {
observers.append(observer)
}
}
let settings = AppSettings()
settings.addObserver(Observer1())
settings.addObserver(Observer2())
settings.interval = 10
虽然 Observable
不能用作参数类型,但从它派生的协议也指定关联类型,可以。
您可以更进一步,使 SettingsObserver
成为 (Int) -> Void
的类型别名。这样你就不需要所有那些不同的 ObserverX
类.
typelias SettingsObserver = (Int) -> Void
addObserver
调用将变为:
settings.addObserver { print("Observer 1 changed to \([=12=])") }
settings.addObserver { print("Observer 2 changed to \([=12=])") }
并且 didSet
中的调用将更改为:
observers.forEach { [=13=](interval) }
另外,我不明白为什么会有Settings
。不能直接让 AppSettings
符合 Observable
吗?我的意思是,我知道程序接口的想法等等,但我觉得这有点过分了......
虽然创建一个您可以添加自己的可观察对象的通用包装器很简单,但您应该改用两个 native 解决方案。
通知。
值更改时,使用
NotificationCenter.default
发送通知。观察员应收听这些通知。通知是生态系统的重要组成部分:class AppSettings { enum Notifications { static let intervalChanged = Notification.Name("AppSettingsIntervalChangedNotification") } var interval: TimeInterval = 0 { didSet { NotificationCenter.default.post(name: Notifications.intervalChanged, object: self) } } } let settings = AppSettings() let observer = NotificationCenter.default.addObserver( forName: AppSettings.Notifications.intervalChanged, object: settings, queue: nil ) { [weak settings] _ in guard let settings = settings else { return } print(settings.interval) } settings.interval = 10
Key-value 观察 (KVO)
如果您从
NSObject
继承您的对象,您可以简单地将直接观察者添加到任何 Obj-C 兼容值:class AppSettings: NSObject { @objc dynamic var interval: TimeInterval = 0 } let settings = AppSettings() let observer: NSKeyValueObservation = settings.observe(\.interval, options: .new) { _, change in print(change.newValue) } settings.interval = 10
为了完整起见,这里有一个简单的通用观察者:
class Observable<ValueType> {
typealias Observer = (ValueType) -> Void
var observers: [Observer] = []
var value: ValueType {
didSet {
for observer in observers {
observer(value)
}
}
}
init(_ defaultValue: ValueType) {
value = defaultValue
}
func addObserver(_ observer: @escaping Observer) {
observers.append(observer)
}
}
class AppSettings {
let interval: Observable<TimeInterval> = Observable(0)
}
let settings = AppSettings()
settings.interval.addObserver { interval in
print(interval)
}
settings.interval.value = 10
请注意,我所有的观察者都是简单的闭包。由于 Java 的限制,Java 使用对象作为观察者的原因主要是历史原因。 Swift.
中不需要Observable
或 Observer
协议