[ ПРИМЕЧАНИЕ. Этот ответ был первоначально сформулирован для Swift 2.2. Он был пересмотрен для Swift 4, включая два важных языковых изменения: первый внешний параметр метода больше не подавляется автоматически, а селектор должен быть явно предоставлен Objective-C.]
Вы можете обойти эту проблему, приведя ссылку на функцию к правильной сигнатуре метода:
let selector = #selector(test as () -> Void)
(Однако, на мой взгляд, вам не следует этого делать. Я считаю эту ситуацию ошибкой, показывающей, что синтаксис Swift для обращения к функциям неадекватен. Я отправил отчет об ошибке, но безрезультатно.)
Просто резюмирую новый #selector
синтаксис:
Цель этого синтаксиса - предотвратить слишком распространенные сбои во время выполнения (обычно «нераспознанный селектор»), которые могут возникнуть при передаче селектора в виде буквальной строки. #selector()
принимает ссылку на функцию , и компилятор проверит, действительно ли функция существует, и разрешит ссылку на селектор Objective-C за вас. Таким образом, вы не можете сразу совершить ошибку.
( РЕДАКТИРОВАТЬ: Хорошо, да, вы можете. Вы можете быть полным болваном и установить цель на экземпляр, который не реализует сообщение действия, указанное в #selector
. Компилятор не остановит вас, и вы рухнете, как в старые добрые времена. вздох ...)
Ссылка на функцию может иметь любую из трех форм:
Голое имя функции. Этого достаточно, если функция однозначная. Так, например:
@objc func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test)
}
Существует только один test
метод, поэтому он #selector
относится к нему, даже если он принимает параметр, а параметр #selector
не упоминается. Разрешенный селектор Objective-C, за кулисами, по-прежнему будет правильно "test:"
(с двоеточием, указывающим параметр).
Имя функции вместе с остальной ее подписью . Например:
func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test(_:))
}
У нас есть два test
метода, поэтому нам нужно различать; нотация test(_:)
преобразуется во вторую , с параметром.
Имя функции с остальной ее сигнатурой или без нее, а также приведение типов для отображения типов параметров. Таким образом:
@objc func test(_ integer:Int) {}
@nonobjc func test(_ string:String) {}
func makeSelector() {
let selector1 = #selector(test as (Int) -> Void)
let selector2 = #selector(test(_:) as (Int) -> Void)
}
Здесь мы перегружены test(_:)
. Перегрузка не может подвергаться Objective-C, так как Objective-C не допускает перегрузки, так что только один из них подвергается воздействию, и мы можем сформировать селектор только для того, который является открытой, поскольку селекторы являются функция Objective-C . Но мы все же должны устранить двусмысленность в том, что касается Свифта, и актерский состав это делает.
(Именно эта лингвистическая особенность используется - на мой взгляд, неправильно - в качестве основы для вышеприведенного ответа.)
Кроме того, вам, возможно, придется помочь Swift разрешить ссылку на функцию, сообщив ему, в каком классе находится функция:
Если класс такой же, как этот, или выше по цепочке суперклассов от этого, дальнейшее разрешение обычно не требуется (как показано в примерах выше); необязательно, вы можете сказать self
, используя точечную нотацию (например #selector(self.test)
, и в некоторых ситуациях вам, возможно, придется это сделать.
В противном случае вы используете либо ссылку на экземпляр, для которого реализован метод, с точечной нотацией, как в этом реальном примере ( self.mp
это MPMusicPlayerController):
let pause = UIBarButtonItem(barButtonSystemItem: .pause,
target: self.mp, action: #selector(self.mp.pause))
... или вы можете использовать имя класса с точечной нотацией:
class ClassA : NSObject {
@objc func test() {}
}
class ClassB {
func makeSelector() {
let selector = #selector(ClassA.test)
}
}
(Это кажется любопытным обозначением, потому что похоже, что вы говорите, что test
это метод класса, а не метод экземпляра, но, тем не менее, он будет правильно преобразован в селектор, и это все, что имеет значение.)