2016-02-24 1 views
4

Я пытаюсь лучше понять протоколы в Swift. В частности, необязательные методы протокола. Я думал, что этот вопрос, возможно, придется делать с моим протоколом определяется/используется в другом файле, но если вы поместите в детской площадке, вы получите тот же вопрос:Значение типа 'X' не имеет члена 'y' - необязательный func в протоколе

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 будет работать, но я хочу, чтобы это значение по умолчанию, если метод не определен.

Как я могу заставить это работать?

ответ

1

Я думаю, что это потому, что компилятор известен NSObject не имеет метода shouldJump, поэтому вызов object.shouldJump?() не имеет смысла. Вы можете бросить object вашего протокола:

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

Swift является безопасным типом языком. Чтобы иметь возможность использовать shouldJump?(), вы сначала должны иметь объект, соответствующий MyProtocol. В этом случае вы можете просто отдать свой тип:

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

Вы также можете сохранить его в переменной:

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

Ваш NSObject соответствует к MyProtocol, а потому, что он не реализует дополнительный метод протокола , компилятор знает, что это не имеет SelectorshouldJump:

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 

Если вы не хотите, чтобы реализовать дополнительный метод в расширении, вы должны бросить ваш NSObject, как MyProtocol и убедитесь, что он отвечает к дополнительному 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 let или guard, чтобы проверить, что 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") 
     } 
    } 
} 
+0

Начиная с версии Xcode версии 7.3 beta (7D152p), используя строковые литералы, поскольку селектор устарел. Эквивалентный вызов теперь object.respondsToSelector (#selector (MyProtocol.shouldJump)). –

+0

@PriceRingo Спасибо, я еще не использую бета-версию, поэтому я не реализовал это изменение. Когда это требуется последней стабильной версией Xcode, я обновлю этот ответ. – JAL

+0

Не могли бы вы также написать код для своего последнего варианта? Как человек с годами Objective-C под мной, но не очень Swift, это похоже на самый интересный подход. :) –

Смежные вопросы