[ ПРИМЕЧАНИЕ. Этот ответ был первоначально сформулирован для 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
это метод класса, а не метод экземпляра, но, тем не менее, он будет правильно преобразован в селектор, и это все, что имеет значение.)
as
let selector = #selector(test as (Void) -> Void)
.test as (Void) -> Void
или более короткий синтаксисtest as () -> ()
?Я хочу добавить недостающее значение: доступ к методу экземпляра извне класса.
class Foo { @objc func test() {} @objc func test(_ sender: AnyObject?) {} }
С точки зрения класса полная сигнатура
test()
метода(Foo) -> () -> Void
, которую вам нужно будет указать, чтобы получитьSelector
.#selector(Foo.test as (Foo) -> () -> Void) #selector(Foo.test(_:))
В качестве альтернативы вы можете обратиться к экземпляру
Selector
s, как показано в исходном ответе.let foo = Foo() #selector(foo.test as () -> Void) #selector(foo.test(_:))
источник
Foo.xxx
уже странные, потому что это не методы внешнего класса. Кажется, компилятор дает вам пропуск, но только в том случае, если нет двусмысленности. Если есть двусмысленность, вам придется закатать рукава и использовать более длинную нотацию, что является законным и точным, потому что метод экземпляра «тайно» является методом каррированного класса. Очень точное обнаружение оставшегося крайнего случая!В моем случае (Xcode 11.3.1) ошибка была только при использовании lldb во время отладки. При запуске работает исправно.
источник