Oli*_*erD 5 arrays closures casting type-signature swift
我正在尝试在Swift(目前是Swift 2)中编写一个轻型观察者类。这个想法是在实体组件系统中使用它,作为组件之间彼此通信而不耦合的一种方式。
我遇到的问题是所有类型的数据都可以通信,a CGVector,an NSTimeInterval等等。这意味着,传递的方法可能有各种型签名(CGVector) -> Void,() -> Void等等。
我希望能够将这些不同的签名存储在数组中,但仍然具有一定的类型安全性。我的想法是数组的类型应该是(Any) -> Void或(Any?) -> Void,这样我至少可以确保它包含方法。但我有这样烦恼的传球方法:Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'。
//: Playground - noun: a place where people can play
import Cocoa
import Foundation
enum EventName: String {
case input, update
}
struct Binding{
let listener: Component
let action: (Any) -> ()
}
class EventManager {
var events = [EventName: [Binding]]()
func add(name: EventName, event: Binding) {
if var eventArray = events[name] {
eventArray.append(event)
} else {
events[name] = [event]
}
}
func dispatch(name: EventName, argument: Any) {
if let eventArray = events[name] {
for element in eventArray {
element.action(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
eventArray = eventArray.filter(){ $0.listener.doc != listener.doc }
}
}
}
// Usage test
//Components
protocol Component {
var doc: String { get }
}
class Input: Component {
let doc = "InputComponent"
let eventManager: EventManager
init(eventManager: EventManager) {
self.eventManager = eventManager
}
func goRight() {
eventManager.dispatch(.input, argument: CGVector(dx: 10, dy: 0) )
}
}
class Movement: Component {
let doc = "MovementComponent"
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
let doc = "PhysicsComponent"
func update(time: NSTimeInterval){
print("updated at \(time)")
}
}
class someClass {
//events
let eventManager = EventManager()
// components
let inputComponent: Input
let moveComponent = Movement()
init() {
inputComponent = Input(eventManager: eventManager)
let inputBinding = Binding(listener: moveComponent, action: moveComponent.move) // ! Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'
eventManager.add(.input, event: inputBinding)
}
}
let someInstance = someClass()
someInstance.inputComponent.goRight()
Run Code Online (Sandbox Code Playgroud)
引发错误Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'。
如果我泛化该Binding结构以识别不同类型的参数,那么我会更幸运。这个版本基本上可以用,但是保存方法的数组现在可以了[Any](我不确定是否是尝试将其强制转换Any回Binding导致下面的稍微奇怪的错误的struct Binary operator '!=' cannot be applied to two 'String' operands):
struct Binding<Argument>{
let listener: Component
let action: (Argument) -> ()
}
class EventManager {
var events = [EventName: [Any]]()
func add(name: EventName, event: Any) {
if var eventArray = events[name] {
eventArray.append(event)
} else {
events[name] = [event]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).action(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
// eventArray = eventArray.filter(){ ($0 as! Binding).listener.doc != listener.doc } //Binary operator '!=' cannot be applied to two 'String' operands
}
}
}
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点,并让数组容纳不同类型签名的方法,例如[(Any?) -> ()]?
环顾四周,例如在这里http://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/看来我的上述方法将导致强大的参考周期,我需要做的是通过静态方法Movement.move而不是moveComponent.move。所以我要存储的类型签名实际上是(Component) -> (Any?) -> Void而不是(Any?) -> Void。但是我的问题仍然存在,我仍然希望能够以比类型更多的类型安全性来存储这些静态方法的数组[Any]。
Casey Fleser 链接到的 Mike Ash 博客中建议的一种强制转换闭包参数的方法是“重复”(?)它。
泛型 Binding 类:
private class Binding<Argument>{
weak var listener: AnyObject?
let action: AnyObject -> Argument -> ()
init(listener: AnyObject, action: AnyObject -> Argument -> ()) {
self.listener = listener
self.action = action
}
func invoke(data: Argument) -> () {
if let this = listener {
action(this)(data)
}
}
}
Run Code Online (Sandbox Code Playgroud)
还有事件管理器,无需重复:
class EventManager {
var events = [EventName: [AnyObject]]()
func add<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
let binding = Binding(listener: listener, action: action) //error: cannot convert value of type 'T -> Argument -> Void' to expected argument type 'AnyObject -> _ -> ()'
if var eventArray = events[name] {
eventArray.append(binding)
} else {
events[name] = [binding]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).invoke(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
eventArray = eventArray.filter(){ $0 !== listener }
}
}
}
Run Code Online (Sandbox Code Playgroud)
这仍然会产生相同的错误,无法转换为AnyObject:
error: cannot convert value of type 'T -> Argument -> Void' to expected argument type 'AnyObject -> _ -> ()'。
如果我们调用柯里化函数的第一部分,并将其包含在一个新的闭包中(我不知道这是否有一个名称,我称之为“递归”),就像这样:那么一切都action: { action($0 as! T) }有效(技术摘自迈克·阿什)。我想这有点黑客行为,因为 Swift 类型安全性被规避了。
我也不太明白错误消息:它说它无法转换T为AnyObject,但随后接受转换为T?
编辑:更新了迄今为止的完整代码edit2:更正了事件的附加方式edit3:删除事件现在可以工作
//: Playground - noun: a place where people can play
import Cocoa
import Foundation
enum EventName: String {
case input, update
}
private class Binding<Argument>{
weak var listener: AnyObject?
let action: AnyObject -> Argument -> ()
init(listener: AnyObject, action: AnyObject -> Argument -> ()) {
self.listener = listener
self.action = action
}
func invoke(data: Argument) -> () {
if let this = listener {
action(this)(data)
}
}
}
class EventManager {
var events = [EventName: [AnyObject]]()
func add<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
let binding = Binding(listener: listener, action: { action($0 as! T) }) //
if events[name]?.append(binding) == nil {
events[name] = [binding]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).invoke(argument)
}
}
}
func remove<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
events[name]? = events[name]!.filter(){ ( $0 as! Binding<Argument>).listener !== listener }
}
}
// Usage test
//Components
class Component {
weak var events: EventManager?
let doc: String
init(doc: String){
self.doc = doc
}
}
class Input: Component {
init() {
super.init(doc: "InputComponent")
}
func goRight() {
events?.dispatch(.input, argument: CGVector(dx: 10, dy: 0) )
}
func goUp() {
events?.dispatch(.input, argument: CGVector(dx: 0, dy: -5) )
}
}
class Movement: Component {
init() {
super.init(doc: "MovementComponent")
}
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
init() {
super.init(doc: "PhysicsComponent")
}
func update(time: NSTimeInterval){
print("updated at \(time)")
}
func move(vector: CGVector) {
print("updated \(vector)")
}
}
// Entity
class Entity {
let events = EventManager()
}
class someClass: Entity {
// components
let inputComponent: Input
let moveComponent: Movement
let physicsComponent: Physics
override init() {
inputComponent = Input()
moveComponent = Movement()
physicsComponent = Physics()
super.init()
inputComponent.events = events
events.add(.input, listener: moveComponent, action: Movement.move)
events.add(.input, listener: physicsComponent, action: Physics.move)
}
}
let someInstance = someClass()
someInstance.inputComponent.goRight()
//moved CGVector(dx: 10.0, dy: 0.0)
//updated CGVector(dx: 10.0, dy: 0.0)
someInstance.events.remove(.input, listener: someInstance.moveComponent, action: Movement.move)
someInstance.inputComponent.goUp()
//updated CGVector(dx: 0.0, dy: -5.0)
someInstance.events.remove(.input, listener: someInstance.physicsComponent, action: Physics.move)
someInstance.inputComponent.goRight()
// nothing
Run Code Online (Sandbox Code Playgroud)