努力在 Swift 5 中编写下面的 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);
}
}
虽然创建一个通用包装器(可以添加自己的可观察量(很简单,但您应该改用两种本机解决方案。
-
通知。
更改值时,使用
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
-
键值观察 (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
请参阅 https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift
为了完整起见,这里有一个简单的通用观察者:
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
协议。
根据您的需求,您可以使用 Swift 中的属性观察器。 它允许您在属性要更改或已更改时采取措施。 它也比完全可观察的类型复杂。
以下是 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 的新 Combine 等框架,您还可以使用属性观察器编写一个简单的类似可观察的类。
下面是一个仅使用闭包而不是类的简单示例:
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 代码。这是我的翻译,带有一定程度的"快速化":
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 { $0.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 ($0)") }
settings.addObserver { print("Observer 2 changed to ($0)") }
didSet
中的呼叫将更改为:
observers.forEach { $0(interval) }
另外,我不明白为什么Settings
存在。你不能直接AppSettings
符合Observable
吗?我的意思是,我知道程序接口的想法等等,但 IMO 这有点太多了......