无法在Swift中使用KeyType类型的参数列表调用方法

时间:2015-06-22 20:07:36

标签: swift generics

我正在使用Swift中的自定义消息调度程序。我想有调度员,可以:

  • 将枚举作为事件或任何Hashable发送。
  • 实现一个协议的每个对象都可以添加为侦听器

以下是代码:

import Foundation

protocol EventDispatcherProtocol: class {
    typealias T: Hashable
    func dispatcherDidDispatchEvent(event: T)
}

class EventListenerWrapper<T: EventDispatcherProtocol> {
    weak var object: T?

    init(_ object: T) {
        self.object = object
    }
}

class EventDispatcher<KeyType: Hashable, U: EventDispatcherProtocol> {
    private var objects = Dictionary<KeyType, [EventListenerWrapper<U>]>()

    func add(#listener: U, forEvent event: KeyType) {
        let wrapper = EventListenerWrapper(object: listener)

        // get all wrappers
        if let storedWrappers = objects[event] {
            // check if there is such objects already wrapped for this type of event
            var found = false
            for storedWrapper in storedWrappers {
                if let storedObject = storedWrapper.object
                where (storedObject as! AnyObject) === (wrapper.object as! AnyObject) {
                    // Found it!
                    found = true
                    break
                }
            }

            if found == false {
                // If not found then add the wrapper for this event
                var updatedWrappers = storedWrappers
                updatedWrappers.append(wrapper)
                self.objects[event] = updatedWrappers
            }

        } else {
            self.objects[event] = [wrapper]
        }
    }

    func dispatch(event: KeyType) {
        // Enumerate through wrappers
        if let storedWrappers = objects[event] {
            var idxToRemove = [Int]()

            var currentIdx = 0
            for storedWrapper in storedWrappers {
                // if there is object to dispatch - dispatch message
                // otherwise add index of this object to remove wrapper later
                if let storedObject = storedWrapper.object {
//                    storedObject.dispatcherDidDispatchEvent(event) /// <- cannot invoke 'this method'  with an argument list of type '(KeyType)'
                } else {
                    idxToRemove.append(currentIdx)
                }

                currentIdx++
            }

            // enumerate through reversed array of indexes to remove
            // and remove items, update wrappers at the end.
            idxToRemove = idxToRemove.reverse()
            var updatedWrappers = storedWrappers
            for idx in idxToRemove {
                updatedWrappers.removeAtIndex(idx)
            }

            objects[event] = updatedWrappers
        }
    }
}



// EXAMPLE

enum CustomEvent {
    case Start, Stop
}

class CustomListener: EventDispatcherProtocol {
    func dispatcherDidDispatchEvent(event: CustomEvent) {
        println("CustomListener heard that event! \(event)")
    }
}

// Create listeners and wrap them
var listener1 = CustomListener()
var listener2 = CustomListener()

// Create dispatcher
var dispatcher = EventDispatcher<CustomEvent, CustomListener>()
dispatcher.add(listener: listener1, forEvent: CustomEvent.Start)
dispatcher.add(listener: listener2, forEvent: CustomEvent.Stop)

dispatcher.dispatch(CustomEvent.Start)
dispatcher.dispatch(CustomEvent.Stop)

不幸的是object.dispatcherDidDispatchEvent(event)来电时出错了。我无法理解这有什么问题。它看起来对我来说正确。

提前谢谢!

编辑 - @Tobol建议后的工作解决方案

import Foundation

protocol EventDispatcherProtocol {
    typealias T: Hashable
    func dispatcherDidDispatchEvent(event: T)
}

class EventListenerWrapper<T: EventDispatcherProtocol> {
    var object: T?

    init(_ object: T) {
        self.object = object
    }
}

class EventDispatcher<U: EventDispatcherProtocol> {
    typealias KeyType = U.T
    private var objects = Dictionary<KeyType, [EventListenerWrapper<U>]>()

    func add(#listener: U, forEvent event: KeyType) {
        let wrapper = EventListenerWrapper(listener)
        // get all wrappers
        if let storedWrappers = objects[event] {
            // check if there is such objects already wrapped for this type of event
            // and if not found add the wrapper for this event
            if storedWrappers.filter({($0.object as? AnyObject) === (listener as! AnyObject) }).count == 0 {
                objects[event] = storedWrappers + [wrapper]
            }

        } else {
            objects[event] = [wrapper]
        }
    }

    func dispatch(event: KeyType) {
        // Enumerate through wrappers
        if var storedWrappers = objects[event] {
            // Enumerate through stored wrappers and get indexes of objects to remove
            var indexesToRemove = [Int]()
            var currentIndex = 0
            storedWrappers.filter({
                var match = $0.object == nil
                if match { indexesToRemove.append(currentIndex) }
                currentIndex++
                return match
            })

            // Reverse array of indexes to make removing process easier
            for index in indexesToRemove.reverse() {
                storedWrappers.removeAtIndex(index)
            }

            // Dispatch message on existing objects
            for wrapper in storedWrappers {
                wrapper.object!.dispatcherDidDispatchEvent(event)
            }

            objects[event] = storedWrappers
        }
    }
}



// EXAMPLE
enum CustomEvent {
    case Start, Stop
}

class CustomListener: EventDispatcherProtocol {
    func dispatcherDidDispatchEvent(event: CustomEvent) {
        println("CustomListener heard that event! \(event)")
    }
}

// Create listeners and wrap them
var listener1 = CustomListener()

// Create dispatcher
var dispatcher = EventDispatcher<CustomListener>()
dispatcher.add(listener: listener1, forEvent: CustomEvent.Start)
dispatcher.dispatch(CustomEvent.Start)

2 个答案:

答案 0 :(得分:1)

storedObject.dispatcherDidDispatchEvent(event) /// <- cannot invoke 'this method'  with an argument list of type '(KeyType)'

event此处的类型为KeyType,这是受Hashable约束的通用类型。

dispatcherDidDispatchEvent()采用CustomEvent类型的参数。这不等于&#34; EventDispatcher专用的可混合类型。&#34;

其中一个需要改变。

答案 1 :(得分:1)

我认为针对您的案例的最佳解决方案是将类声明更改为:

class EventDispatcher<U: EventDispatcherProtocol> {
    typealias KeyType = U.T

它还将通过跳过冗余类型声明来简化EventDispatcher的创建:

var dispatcher = EventDispatcher<CustomListener<CustomEvent>>()

修改 由于在编写答案时代码被多次更改,因此我附上整个解决方案代码示例:

import Foundation

protocol EventDispatcherProtocol {
    typealias T: Hashable
    func dispatcherDidDispatchEvent(event: T)
}

class EventListenerWrapper<T: EventDispatcherProtocol> {
    var object: T?

    init(object: T) {
        self.object = object
    }
}

class EventDispatcher<U: EventDispatcherProtocol> {
    typealias KeyType = U.T

    private var objects = Dictionary<KeyType, [EventListenerWrapper<U>]>()

    func add(#listener: U, forEvent event: KeyType) {
        let wrapper = EventListenerWrapper(object: listener)

        // get all wrappers
        if let storedWrappers = objects[event] {
            // check if there is such objects already wrapped for this type of event
            var found = false
            for storedWrapper in storedWrappers {
                if let storedObject = storedWrapper.object
                    where (storedObject as! AnyObject) === (wrapper.object as! AnyObject) {
                        // Found it!
                        found = true
                        break
                }
            }

            if found == false {
                // If not found then add the wrapper for this event
                var updatedWrappers = storedWrappers
                updatedWrappers.append(wrapper)
                self.objects[event] = updatedWrappers
            }

        } else {
            self.objects[event] = [wrapper]
        }
    }

    func dispatch(event: KeyType) {
        // Enumerate through wrappers
        if let storedWrappers = objects[event] {
            var idxToRemove = [Int]()

            var currentIdx = 0
            for storedWrapper in storedWrappers {
                // if there is object to dispatch - dispatch message
                // otherwise add index of this object to remove wrapper later
                if let storedObject = storedWrapper.object {
                    storedObject.dispatcherDidDispatchEvent(event) /// <- cannot invoke 'this method'  with an argument list of type '(KeyType)'
                } else {
                    idxToRemove.append(currentIdx)
                }

                currentIdx++
            }

            // enumerate through reversed array of indexes to remove
            // and remove items, update wrappers at the end.
            idxToRemove = idxToRemove.reverse()
            var updatedWrappers = storedWrappers
            for idx in idxToRemove {
                updatedWrappers.removeAtIndex(idx)
            }

            objects[event] = updatedWrappers
        }
    }
}



// EXAMPLE

enum CustomEvent {
    case Start, Stop
}

class CustomListener<T: Hashable>: EventDispatcherProtocol {
    func dispatcherDidDispatchEvent(event: T) {
        println("CustomListener heard that event! \(event)")
    }
}

// Create listeners and wrap them
var listener1 = CustomListener<CustomEvent>()
var listener2 = CustomListener<CustomEvent>()

// Create dispatcher
var dispatcher = EventDispatcher<CustomListener<CustomEvent>>()
dispatcher.add(listener: listener1, forEvent: CustomEvent.Start)
dispatcher.add(listener: listener2, forEvent: CustomEvent.Stop)

dispatcher.dispatch(CustomEvent.Start)
dispatcher.dispatch(CustomEvent.Stop)