[ ПРИМІТКА Ця відповідь спочатку була сформульована в Swift 2.2. Його було переглянуто для Swift 4, включаючи дві важливі зміни мови: перший параметр методу external більше не припиняється автоматично, і селектор повинен бути явно підданий дії 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
, що це метод класу, а не метод екземпляра, але він, тим не менше, буде правильно розв'язаний до селектора, що є лише важливим.)