Ответ 1
[ ПРИМЕЧАНИЕ. Этот ответ был изначально сформулирован в Swift 2.2. Он был пересмотрен для Swift 4 и включает два важных языковых изменения: первый параметр метода external больше не подавляется автоматически, а селектор должен быть явно представлен Objective-C.]
Вы можете обойти эту проблему, приведя ссылку на функцию к правильной сигнатуре метода:
let selector = #selector(test as () -> Void)
(Однако, по моему мнению, вам не нужно этого делать. Я рассматриваю эту ситуацию как ошибку, обнаруживая, что синтаксис Swift для ссылки на функции неадекватен. Я подал отчет об ошибке, но безрезультатно.)
Просто #selector
итог новому синтаксису #selector
:
Целью этого синтаксиса является предотвращение слишком распространенных сбоев во время выполнения (обычно "нераспознанный селектор"), которые могут возникнуть при предоставлении селектора в виде литеральной строки. #selector()
принимает ссылку на функцию, и компилятор проверит, что функция действительно существует, и разрешит ссылку на селектор Objective-C для вас. Таким образом, вы не можете легко ошибиться.
(РЕДАКТИРОВАТЬ: Хорошо, да, вы можете. Вы можете быть полным ланчем и установить цель в качестве экземпляра, который не реализует сообщение действия, указанное #selector
. Компилятор не остановит вас, и вы #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(_:)
обозначенийtest(_:)
разрешает второй, тот, который с параметром. -
Имя функции с или без остальной ее подписи, а также приведение, чтобы показать типы параметров. Таким образом:
@objc func test(_ integer:Int) {} @nonobjc func test(_ string:String) {} func makeSelector() { let selector1 = #selector(test as (Int) -> Void) // or: 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
- это метод класса, а не метод экземпляра, но, тем не менее, он будет корректно разрешен для селектора, и это все, что имеет значение.)