使用 runtime-specified 协议一致性测试和选择器的通用调度机制
Generic dispatch mechanism using runtime-specified Protocol conformance test & selector
所以,我正在制作 SpriteKit 游戏,我想转发 update
、mouseDown
、keyDown
等...在 [=17 上调用的事件=],该场景的 children,前提是它们是具有适当响应者的 SKNode 的自定义子类。
我根据我想在每个节点上调用哪些函数制定了几个协议,所以就像我有 KeyboardDelegate
用于需要接收 keyDown
、keyUp
等的节点。 .. UpdateDelegate
用于 update
、didSimulatePhysics
等...对于每种类型的调用,我总共有 UpdateDelegate
、KeyboardDelegate
、MouseDelegate
和 CollisionDelegate
各自对应的调用类型。
所以在 GameScene
我有这样的功能:
func someEvent(someParameter: ArgumentType) {
for child in children where child is SomeProtocol {
//onSomeEvent should be required by SomeProtocol
(child as! SomeProtocol).onSomeEvent(someParameter)
}
}
问题是我想转发这些事件中的大约 20 个,并且我在每个函数中都有相同的代码,唯一不同的部分是 SomeProtocol
和 onSomeEvent
。
我试图创建一个函数来处理所有这些,这样我就不需要像这样为每个要转发的事件编写代码:
extension SKNode {
func forward<T>(method: Selector, onChildrenOfType: T.Type, withArgument argument: AnyObject) {
for child in children where child is T {
child.performSelector(method, withObject: first)
}
}
}
分机在 SKNode 上,所以我可以在每个 children 接收呼叫的每个单独节点上转发呼叫,无论它们是什么类型的 SKNode。
这个函数本身好像不太对,也不是因为它不起作用,我不使用SomeProtocol.self
就不能直接通过协议,每当我这样调用它的时候:
override func someEvent(someParameter: ArgumentType) {
self.recurse(#selector(onSomeEvent(_:)), onChildrenOfType: SomeProtocol.self, withArgument: someParameter)
}
我会得到一个 use of unresolved identifier 'onSomeEvent'
,我假设是因为编译器不知道在哪里寻找 onSomeEvent
函数。
所以...有没有办法使这项工作(除了可能使用旧的选择器语法之外)最好是我可以传递超过 2 个参数(因为 performSelector 只允许最多 2 个,并且 objc_msgSend
和 NSInvocation
不可用),还是我应该坚持在每个要转发的事件中复制代码。
所以这很有趣而且相当棘手。关键是测试作为参数传入的 runtime- 协议类型——这是元数据,在使用编译时一致性检查以提高效率的 Swift 中是不可能的& 安全。所以首先你需要在 Obj-C 运行时中建立所有东西,这里的关键是 conformsToProtocol
方法。如果您需要这样一个高度通用的调度,我不会尝试传递超过 2 个参数:而是传入某种容器(如 NSNotification
的 userInfo: NSDictionary
),其中包含您需要的值。通过使用 performSelector:
的 withObject:
变体,下面的代码可以很容易地适应带有 1 个参数的函数,我相信你知道。请注意,选择器函数 必须 return 一个对象,否则您的代码将严重崩溃,所以我在这里选择 self
是为了方便(和可链接性!)
import Foundation
// performSelector methods must return a reference to something
@objc protocol P1 {
func doP1Action() -> P1
}
@objc protocol P2 {
func doP2Action() -> P2
}
class C1a: NSObject, P1 {
func doP1Action() -> P1 { print("I'm doing C1a's impl of a P1 Action"); return self }
}
class C1b: NSObject, P1 {
func doP1Action() -> P1 { print("I'm doing C1b's impl of a P1 Action"); return self }
}
class C2a: NSObject, P2 {
func doP2Action() -> P2 { print("I'm doing C2a's impl of a P2 Action"); return self }
}
class C2b: NSObject, P2 {
func doP2Action() -> P2 { print("I'm doing C2b's impl of a P2 Action"); return self }
}
var children = [ C1a(), C2b(), C1b(), C1a(), C2a() ]
func performAction(action: Selector, forObjectsConformingTo conform: Protocol, inArray array: [AnyObject]) -> [AnyObject] {
return array.flatMap { [=10=].conformsToProtocol(conform) ? [=10=].performSelector(action).takeRetainedValue() : nil }
}
// And now the fully generic reusable logic:
print("Performing actions on P1's:")
performAction(#selector(P1.doP1Action), forObjectsConformingTo: P1.self, inArray: children)
print("Performing actions on P2's:")
performAction(#selector(P2.doP2Action), forObjectsConformingTo: P2.self, inArray: children)
//Performing actions on P1's:
//I'm doing C1a's impl of a P1 Action
//I'm doing C1b's impl of a P1 Action
//I'm doing C1a's impl of a P1 Action
//Performing actions on P2's:
//I'm doing C2b's impl of a P2 Action
//I'm doing C2a's impl of a P2 Action
就递归而言,这是最简单的部分,与导航类型演算或基础无关 API。您只需在 forEach
正文中对 self
.
的子项进行递归调用
所以,我正在制作 SpriteKit 游戏,我想转发 update
、mouseDown
、keyDown
等...在 [=17 上调用的事件=],该场景的 children,前提是它们是具有适当响应者的 SKNode 的自定义子类。
我根据我想在每个节点上调用哪些函数制定了几个协议,所以就像我有 KeyboardDelegate
用于需要接收 keyDown
、keyUp
等的节点。 .. UpdateDelegate
用于 update
、didSimulatePhysics
等...对于每种类型的调用,我总共有 UpdateDelegate
、KeyboardDelegate
、MouseDelegate
和 CollisionDelegate
各自对应的调用类型。
所以在 GameScene
我有这样的功能:
func someEvent(someParameter: ArgumentType) {
for child in children where child is SomeProtocol {
//onSomeEvent should be required by SomeProtocol
(child as! SomeProtocol).onSomeEvent(someParameter)
}
}
问题是我想转发这些事件中的大约 20 个,并且我在每个函数中都有相同的代码,唯一不同的部分是 SomeProtocol
和 onSomeEvent
。
我试图创建一个函数来处理所有这些,这样我就不需要像这样为每个要转发的事件编写代码:
extension SKNode {
func forward<T>(method: Selector, onChildrenOfType: T.Type, withArgument argument: AnyObject) {
for child in children where child is T {
child.performSelector(method, withObject: first)
}
}
}
分机在 SKNode 上,所以我可以在每个 children 接收呼叫的每个单独节点上转发呼叫,无论它们是什么类型的 SKNode。
这个函数本身好像不太对,也不是因为它不起作用,我不使用SomeProtocol.self
就不能直接通过协议,每当我这样调用它的时候:
override func someEvent(someParameter: ArgumentType) {
self.recurse(#selector(onSomeEvent(_:)), onChildrenOfType: SomeProtocol.self, withArgument: someParameter)
}
我会得到一个 use of unresolved identifier 'onSomeEvent'
,我假设是因为编译器不知道在哪里寻找 onSomeEvent
函数。
所以...有没有办法使这项工作(除了可能使用旧的选择器语法之外)最好是我可以传递超过 2 个参数(因为 performSelector 只允许最多 2 个,并且 objc_msgSend
和 NSInvocation
不可用),还是我应该坚持在每个要转发的事件中复制代码。
所以这很有趣而且相当棘手。关键是测试作为参数传入的 runtime- 协议类型——这是元数据,在使用编译时一致性检查以提高效率的 Swift 中是不可能的& 安全。所以首先你需要在 Obj-C 运行时中建立所有东西,这里的关键是 conformsToProtocol
方法。如果您需要这样一个高度通用的调度,我不会尝试传递超过 2 个参数:而是传入某种容器(如 NSNotification
的 userInfo: NSDictionary
),其中包含您需要的值。通过使用 performSelector:
的 withObject:
变体,下面的代码可以很容易地适应带有 1 个参数的函数,我相信你知道。请注意,选择器函数 必须 return 一个对象,否则您的代码将严重崩溃,所以我在这里选择 self
是为了方便(和可链接性!)
import Foundation
// performSelector methods must return a reference to something
@objc protocol P1 {
func doP1Action() -> P1
}
@objc protocol P2 {
func doP2Action() -> P2
}
class C1a: NSObject, P1 {
func doP1Action() -> P1 { print("I'm doing C1a's impl of a P1 Action"); return self }
}
class C1b: NSObject, P1 {
func doP1Action() -> P1 { print("I'm doing C1b's impl of a P1 Action"); return self }
}
class C2a: NSObject, P2 {
func doP2Action() -> P2 { print("I'm doing C2a's impl of a P2 Action"); return self }
}
class C2b: NSObject, P2 {
func doP2Action() -> P2 { print("I'm doing C2b's impl of a P2 Action"); return self }
}
var children = [ C1a(), C2b(), C1b(), C1a(), C2a() ]
func performAction(action: Selector, forObjectsConformingTo conform: Protocol, inArray array: [AnyObject]) -> [AnyObject] {
return array.flatMap { [=10=].conformsToProtocol(conform) ? [=10=].performSelector(action).takeRetainedValue() : nil }
}
// And now the fully generic reusable logic:
print("Performing actions on P1's:")
performAction(#selector(P1.doP1Action), forObjectsConformingTo: P1.self, inArray: children)
print("Performing actions on P2's:")
performAction(#selector(P2.doP2Action), forObjectsConformingTo: P2.self, inArray: children)
//Performing actions on P1's:
//I'm doing C1a's impl of a P1 Action
//I'm doing C1b's impl of a P1 Action
//I'm doing C1a's impl of a P1 Action
//Performing actions on P2's:
//I'm doing C2b's impl of a P2 Action
//I'm doing C2a's impl of a P2 Action
就递归而言,这是最简单的部分,与导航类型演算或基础无关 API。您只需在 forEach
正文中对 self
.