首页 > 解决方案 > 在 Swift 中,为给定基类的未指定子类创建“包装器”子类的语法是什么?

问题描述

这让我发疯了,因为我觉得它应该是可能的,但无论我如何表达这个想法,我都无法让编译器满意......

我正在做一个 SpriteKit 项目。其中一个类是 SKSpriteNode 的包装器,用于添加精灵在被触摸时调用的动作:

class TouchableSprite: SKSpriteNode {
  /// The action to run upon touch
  var action: (() -> Void)? = nil

  /// Make the sprite react to touches
  override var isUserInteractionEnabled: Bool {
    get { return true }
    set {}
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    action?()
  }
}

一切都很好。现在我想要一个具有相同功能的 SKLabelNode。只是复制代码但使用 SKLabelNode 可以工作,但我认为我应该能够拥有一个适用于任何各种 SKNode 子类的通用包装器。所以...

class Touchable<T>: T where T: SKNode { ...

不,从非协议、非类类型“T”继承。

class Touchable<T: SKNode>: T { ...

同样的交易......哦,我知道!

class Touchable: T where T: SKNode { ...

至少这是一个不同的错误:“where”子句不能附加到非泛型声明。

class Touchable<T>: T where <T: SKNode> { ...

不,闭包表达式未使用。在经历了无数次不同的尝试之后,我觉得自己是世界上最大的白痴,不得不问如何写这个,但我真的很想知道。

哦,我还认为 SpriteKit 的类层次结构可能有些奇怪(因为双重 Swift/Objective C 支持),所以我还尝试了仅使用纯 Swift 类的各种排列。

class Base {
}

class Wrapper<T: Base>: T {
}

相同的消息数组;我只是无法做到这一点。有人可以告诉我如何表达这个想法,或者至少告诉我为什么不可能。

编辑:至少对于我有节点层次结构的 SpriteKit,我目前的解决方法是将包装器添加为父节点:

class Touchable: SKNode {
  let action: () -> Void

  init(_ child: SKNode, _ action: @escaping () -> Void) {
    self.action = action
    super.init()
    name = "touchable" + (child.name ?? "")
    addChild(child)
  }

  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented by Touchable")
  }

  override var isUserInteractionEnabled: Bool {
    get { return true }
    set {}
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    action()
  }
}

然后我做任何我想包装的东西,而不是addChild(...)我做的addChild(Touchable(wrapMe) { ... })。没关系,但没有我想要的那么干净。

标签: swift

解决方案


这在 Swift 或 ObjC 中是不可能的。您正在尝试创建多重继承,这是不允许的。扩展不能覆盖方法,所以你不能通过扩展来做到这一点。你不能让一个类型继承自 SKSpriteNode 和“提供覆盖的其他类型(即 Touchable)”。而且你不能(安全地)将 Touchable 插入到 SKSpriteNode 的超类链中(你实际上可以使用运行时,但不要这样做;你可以很容易地以意想不到的方式破坏系统的其余部分)。

您的“解决方法”实际上是执行此操作的正确方法。这不是一种解决方法。这只是作曲,而且很好。如果您觉得使用不方便,我们可以谈谈;如果这是一个问题,使用扩展来改进调用语法可能很简单。


推荐阅读