6

我试图更好地理解 Swift 中的协议。特别是可选的协议方法。我认为这个问题可能与我在不同文件中定义/使用的协议有关,但如果你将以下内容放在操场上,你会遇到同样的问题:

import Foundation

@objc protocol MyProtocol {
    optional func shouldJump() -> Bool
}

extension NSObject : MyProtocol {}

class Test {
    func testJump() {
        let object = NSObject()
        let jump = object.shouldJump?() ?? true

        print("should jump: \(jump)")
    }
}

let t = Test()
t.testJump()

这是错误消息:

error: value of type 'NSObject' has no member 'shouldJump'
            let jump = object.shouldJump?() ?? true
                       ^~~~~~ ~~~~~~~~~~

出于某种原因,它不接受协议已在 NSObject 上定义。代码完成找到它,但编译器不让它通过。

我不确定我的?? true部分是否有效,但我希望这是一个默认值,以防方法未定义。

我怎样才能让它工作?

4

3 回答 3

2

NSObject符合MyProtocol,但因为它没有实现可选协议方法,编译器知道它没有Selector shouldJump

let object = NSObject()
object.conformsToProtocol(MyProtocol) // true
object.respondsToSelector("shouldJump") // false

解决这个问题的一种方法是在扩展中实现协议方法,以便对象执行该选择器:

extension NSObject : MyProtocol {
    func shouldJump() -> Bool {
        // some logic here
        return true
    }
}

class Test {
    func testJump() {
        let object = NSObject()

        let jump = object.shouldJump()

        print("should jump: \(jump)")
    }
}

let t = Test()
t.testJump() // works

如果您不想在扩展中实现 optional 方法,则必须强制转换NSObjectasMyProtocol并验证它是否响应 optional Selector

class Test {
    func testJump() {
        let object = NSObject()

        let obj = object as MyProtocol

        if object.respondsToSelector("shouldJump")  {
            let jump = obj.shouldJump?()

            print("should jump: \(jump)")
        } else {
            print("nope")
        }


    }
}

您也可以跳过该respondsToSelector步骤并使用if letorguard来验证是否shouldJump()返回非零。

class Test {
    func testJump() {
        let object = NSObject()

        guard let obj: MyProtocol = object else {
            return // object does not conform to MyProtocol
        }

        if let jump = obj.shouldJump?() { // if shouldJump() returns non-nil
            print("should jump: \(jump)")
        } else {
            print("nope")
        }
    }
}
于 2016-02-24T02:21:18.770 回答
1

我认为这是因为编译器知道 NSObject 没有shouldJump方法,所以调用object.shouldJump?()没有意义。您可以object转换为您的协议:

let jump = (object as MyProtocol).shouldJump?() ?? true
于 2016-02-24T02:14:31.203 回答
1

Swift 是一种类型安全的语言。为了能够使用shouldJump?()你首先必须有一个符合MyProtocol. 在这种情况下,您可以简单地转换您的类型:

let jump = (object as MyProtocol).shouldJump?() ?? true

您也可以将其存储在变量中:

let jumper = object as MyProtocol
let jump = jumper?.shouldJump() ?? true
于 2016-02-24T02:15:16.557 回答