Swift中有关协议和泛型的限制

时间:2015-07-12 16:20:39

标签: swift generics

有两种相互作用的泛型类集是一种好的设计模式。一个简单的例子是Observable-Observer模式。可观察者向观察者发布事件,但无论观察到什么类型的事件,模式都是相同的。

我首先想到的是,首选的方法是定义两个通用协议。这应该提供最小的耦合,随着代码库的增长,这种耦合往往很好。

protocol ProtocolObserver {
    typealias EventType
    func update<O:ProtocolObservable where O.EventType == EventType>(observable:O, event:EventType) -> Void
}

protocol ProtocolObservable {
    typealias EventType
    func registerObserver<O:ProtocolObserver where O.EventType == EventType>(observer:O) -> Bool
    func unregisterObserver<O:ProtocolObserver where O.EventType == EventType>(observer:O) -> Void
}

尝试定义实现上述协议的类被证明是一个受伤的世界。我没有找到任何办法。

然而,实现通用基类将是一种可接受的解决方案。

protocol GenericObserver {
    func update<EventType>(observable:GenericObservable<EventType>, event:EventType);
}

class GenericObservable<EventType> {
    private var observers:[GenericObserver] = []
    func registerObserver(observer:GenericObserver) -> Bool {
        // Code to avoid registering the same observer twice
        observers.append(observer)
        return true
    }
    func unregisterObserver(observer:GenericObserver) -> Void {
        // Code to remove the observer if present in observers
    }
    func notifyObservers(event:EventType) -> Void {
        for observer in observers {
            observer.update(self, event: event)
        }
    }
}

这次定义一些实现协议的类没问题。将它们添加到通用观察者的实例中并没有显示我期望的行为。

let numberObservable = GenericObservable<NSNumber>()

class NumberObserver : GenericObserver {
   func update<NSNumber>(observable:GenericObservable<NSNumber>, event:NSNumber) {
       print("Number Event \(event)")
   }
}

let numberObserver = NumberObserver()

numberObservable.registerObserver(numberObserver)

class DataObserver : GenericObserver {
    func update<NSData>(observable:GenericObservable<NSData>, event:NSData) {
        print("Data Event \(event)")
    }
}

let dataObserver = DataObserver()

numberObservable.registerObserver(dataObserver)

numberObservable.notifyObservers(NSNumber(int: 42))

我希望numberObservable.registerObserver(dataObserver)导致编译错误。相反,它愉快地打印输出

Number Event 42
Data Event 42

这让我有两个问题:

  1. 当我希望编译器不接受numberObservable.registerObserver(dataObserver)时,我误解了什么?

  2. 有没有办法实现分别符合ProtocolObserverProtocolObservable的一对类?

1 个答案:

答案 0 :(得分:4)

你的问题1和2实际上是密切相关的。

在开始之前,我应该指出,当你拥有第一类函数时,observable / observer模式几乎完全是多余的。您可以只提供一个闭包,而不是强制要求回调接口。我将在问题2的答案中表明这一点。

首先,1。您遇到的问题是类型擦除。您的基类是您定义registerObserver的唯一位置,它看起来像这样:

class GenericObservable<EventType> {
    private var observers:[GenericObserver] = []
    func registerObserver(observer:GenericObserver) -> Bool {
        // Code to avoid registering the same observer twice
        observers.append(observer)
        return true
    }
   //...
}

也就是说,它将采用并存储任何类型的协议引用。这种类型没有约束,它可以是任何类型。例如,您可以通知Int

extension Int: GenericObserver {
    func update<EventType>(observable:GenericObservable<EventType>, event:EventType) {
        print("Integer \(self)")
    }
}

numberObservable.registerObserver(2)

当被调用者尝试使用EventType时会出现问题。 EventType可以任何。它类似于这个功能:

func f<T>(t: T) { }

T可以是您喜欢的任何类型 - StringIntFoo。但是你无法用它做任何事情,因为它提供零保证。要使泛型类型有用,您必须约束它(即保证它具有某些特征,例如可以添加/减去它的IntegerType),或者将其传递给另一个类似的泛型函数不受限制的(例如将其置于通用集合中,或者调用printunsafeBitCast,它将以任何类型运行)。

基本上,你的观察者都宣称“我有一系列方法,update,你可以用你喜欢的任何类型打电话”。这不是很有用,除非你写的是像map这样的东西或类似于数组的泛型集合,在这种情况下你不关心T是什么。

这可能有助于消除一些困惑 - 这按照您的想法行事:

class DataObserver : GenericObserver {
    func update<NSData>(observable:GenericObservable<NSData>, event:NSData) {
        print("Data Event \(event)")
    }
}

这里有 not 声明DataObserver专门接受NSData课程。您刚刚命名了通用占位符NSData。与命名变量NSData类似 - 它并不意味着变量是什么,只是你所谓的变量。你可以写下这个:

class DataObserver : GenericObserver {
    func update<Bork>(observable:GenericObservable<Bork>, event: Bork) {
        print("Data Event \(event)")
    }
}

好的,如何实现具有相关类型的可观察协议(即协议中的类型)。这是一个例子。但请注意,没有Observer协议。相反,Observable将接受任何接收适当事件类型的函数。

protocol Observable {
    typealias EventType
    func register(f: EventType->())
}
// No need for an "Observer" protocol

现在,让我们实现这一点,将EventType修改为Int

struct FiresIntEvents {
    var observers: [Int->()] = []

    // note, this sets the EventType typealias
    // implicitly via the types of the argument
    mutating func register(f: Int->()) {
        observers.append(f)
    }

    func notifyObservers(i: Int) {
        for f in observers {
            f(i)
        }
    }
}

var observable = FiresIntEvents()

现在,如果我们想通过课堂观察,我们可以:

class IntReceiverClass {
    func receiveInt(i: Int) {
        print("Class received \(i)")
    }
}

let intReceiver = IntReceiverClass()
// hook up the observing class to observe
observable.register(intReceiver.receiveInt)

但我们也可以注册任意函数:

observable.register { print("Unowned closure received \($0)") }

或者在同一个接收器上注册两个不同的功能:

extension IntReceiverClass {
    func recieveIntAgain(i: Int) {
        print("Class recevied \(i) slightly differently")
    }
}

observable.register(intReceiver.recieveIntAgain)

现在,当您触发事件时:

observable.notifyObservers(42)

你得到以下输出:

Class received 42
Unowned closure received 42
Class recevied 42 slightly differently

但是使用这种技术,如果您尝试注册错误事件类型的函数,则会出现编译错误:

observable.register(IntReceiverClass.receiveString)
// error: cannot invoke 'register' with an argument list of type '(IntReceiverClass -> (String) -> ())