在超级中调用swift可选协议方法

时间:2015-04-14 14:53:50

标签: cocoa swift

我想创建一个NSView子类,通过将拖放重定向到其视图控制器来处理拖放。所以我必须覆盖NSView子类中的NSDraggingDestination协议方法。在那里,我想检查视图控制器实现是否支持方法并调用它,或者如果不支持调用基类实现。第一部分看起来很容易通过可选链接,但我如何检查方法是否在超类中实现?例如,这就是我为拖拽方法而提出的。如果视图控制器没有实现draggingEnded方法,它会在块内部发生运行时崩溃。

class ControllerChainedView: NSView {

    @IBOutlet weak var chainedController: NSViewController!

    override func draggingEnded(sender: NSDraggingInfo?) {
        let destination = chainedController as! NSDraggingDestination
        if !(destination.draggingEnded?(sender) != nil) {
           super.draggingEnded(sender);
       }
    }
}

将内部的行更改为super.draggingEnded?(sender);给出编译器错误。 (postfix的操作数'?'应该有可选类型;类型是'(NSDraggingInfo?) - > Void')

类似的draggingEntered方法没有问题,因为它似乎在NSView中实现。 所以问题是如何检测一个人是否可以调用超级方法?

2 个答案:

答案 0 :(得分:0)

如果您的super基于NSObject,您仍然可以这样使用respondsToSelector:

if super.respondsToSelector("draggingEnded:") {
  super.draggingEnded(sender)
}

更新 - 基于第一次Alex评论

取决于。一般情况下不行,你不能在没有调用它的情况下使用可选链接来执行此操作。

但你可以做的是检查功能是否被调用。但同样,仅在特定情况下。必须是@objc协议,必须基于NSObject并且可选函数的返回值不能是可选的。

检查以下示例。

@objc protocol Opt {
  optional func implemented() -> Void
  optional func notImplemented() -> Void
}

class Object: NSObject, Opt {
  func implemented() {}
}

let o = Object() as Opt
let implementedCalled = o.implemented?() != nil // == true
let notImplementedCalled = o.notImplemented?() != nil // == false

在这种特殊情况下,implementedCalled设置为true。换句话说,您至少可以检查方法是否被调用。这是因为可选链接,因为o.implemented返回类型为Voido.implemented?()会返回Void?

您也可以这样做......

if o.implemented != nil {
  // Function is implemented AND implemented WAS CALLED
} else {
  // Function is not implemented
}

...但是不要忘记它不是检查函数是否已实现,它是 - 如果实现了函数,则调用它并返回.Some(Void).None如果它没有实现。

在您的特定情况下,您可以这样做:

override func draggingEnded(sender: NSDraggingInfo?) {
  let destination = chainedController as! NSDraggingDestination
  if destination.draggingEnded?(sender) == nil {
    // .None returned - method not implemented in destination

    // draggingEnded is called in super only if it's implemented
    // ignore result of type Void?
    super.draggingEnded?(sender)
  }
}

如果可选功能的返回类型不是Void,而是例如String,则其行为方式相同。

@objc protocol Opt {
  optional func name() -> String
}

class Object: NSObject, Opt {
  func name() -> String { return "Object" }
}

let o = Object() as Opt
let n: String? = o.name?() // n contains "Object"

如果name未实施,则n将包含None

到目前为止,这么好。我们可以检查是否为VoidString(或任何其他类型)调用了函数。

但是如果返回类型是可选的呢?例如String?

@objc protocol Opt {
  optional func name() -> String?
}

class Object: NSObject, Opt {
  func name() -> String? { return nil }
}

class Object2: NSObject, Opt {
}

let o = Object() as Opt
let n = o.name?()

let o2 = Object2() as Opt
let n2 = o2.name?()

Object实现了name,但它返回nilObject2根本没有实现name。但nn2都包含nil。换句话说,如果返回类型是可选的,我们无法检查函数是否已实现。

正如你所看到的,这很棘手。有时它适合您的需求,有时不适合。如果您确定自己的对象基于NSObject,请坚持.respondsToSelector以确保安全。然后,您可以忽略所有这些条件和案例。

如果您的目标是只调用函数并且您不关心它是否真的实现了,如果您对结果不感兴趣,可以使用?来完成。

答案 1 :(得分:0)

由于可选语法?不适用于super,因此您必须明确检查超类是否实现该方法。

if NSView.instancesRespondToSelector("draggingEnded:") {
    super.draggingEnded(sender)
}