Сам Swift не використовує селектори - кілька моделей дизайну, які в Objective-C використовують селектори, по-різному працюють у Swift. (Наприклад, замість них використовуйте необов'язкові ланцюжки на типи протоколів або is
/ as
тести respondsToSelector:
та використовуйте закриття, де можна, замість performSelector:
кращої безпеки типу / пам’яті.)
Але є ще ряд важливих API на основі ObjC, які використовують селектори, включаючи таймери та шаблон цілі / дії. Swift надає Selector
тип роботи з ними. (Swift автоматично використовує це замість типу ObjC SEL
.)
У Swift 2.2 (Xcode 7.3) та пізніших версіях (включаючи Swift 3 / Xcode 8 та Swift 4 / Xcode 9):
Ви можете побудувати а Selector
з типу функції Swift, використовуючи #selector
вираз.
let timer = Timer(timeInterval: 1, target: object,
selector: #selector(MyClass.test),
userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
with: button, with: otherButton)
Чудова річ у цьому підході? Посилання на функцію перевіряється компілятором Swift, тож ви можете використовувати #selector
вираз тільки з парами класів / методів, які існують і є допустимими для використання в якості селекторів (див. "Доступність селектора" нижче). Ви також можете зробити посилання на свою функцію лише настільки конкретною, скільки вам потрібно, відповідно до правил Swift 2.2+ для іменування типу функції .
(Це насправді вдосконалення щодо @selector()
директиви ObjC , оскільки -Wundeclared-selector
перевірка компілятора підтверджує лише наявність названого селектора. Посилання функції Swift, яке ви передаєте, #selector
перевіряє наявність, членство в класі та підпис типу.)
Є кілька додаткових застережень для посилань на функцію, які ви передаєте #selector
виразу:
- Кілька функцій з тим самим базовим іменем можна диференціювати за їх мітками параметрів, використовуючи вищезазначений синтаксис для посилань на функції (наприклад,
insertSubview(_:at:)
vs insertSubview(_:aboveSubview:)
). Але якщо функція не має параметрів, єдиний спосіб роз'єднати її - використовувати as
команду з підписом типу функції (наприклад, foo as () -> ()
vs foo(_:)
).
- У Swift 3.0+ є спеціальний синтаксис для пар власників / сеттер. Наприклад, давши a
var foo: Int
, ви можете використовувати #selector(getter: MyClass.foo)
або #selector(setter: MyClass.foo)
.
Загальні примітки:
Випадки, коли #selector
не працює, і іменування: Іноді у вас немає посилання на функцію, щоб зробити селектор (наприклад, методами, динамічно зареєстрованими в ObjC час виконання). У цьому випадку ви можете побудувати a Selector
з рядка: напрSelector("dynamicMethod:")
- хоча ви втратите перевірку дійсності компілятора. Коли ви робите це, вам потрібно дотримуватися правил іменування ObjC, включаючи кольори ( :
) для кожного параметра.
Доступність селектора: Метод, на який посилається селектор, повинен піддаватися виконанню ObjC. У Swift 4 кожен метод, що піддається впливу ObjC, повинен мати своє декларування попереднім@objc
атрибутом. (У попередніх версіях ви отримали цей атрибут безкоштовно в деяких випадках, але тепер вам це потрібно чітко заявити.)
Пам’ятайте, що private
символи також не піддаються виконанню - ваш метод повинен мати принаймніinternal
видимість.
Основні шляхи: вони пов'язані, але не зовсім такі ж, як селектори. Для Swift 3 теж є спеціальний синтаксис: напр chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. Детальніше див. У SE-0062 . І ще більше KeyPath
матеріалів у Swift 4 , тому переконайтесь, що ви використовуєте правильний API на основі KeyPath замість селекторів, якщо це доречно.
Докладніше про селектори можна прочитати у розділі Взаємодія з API-кодами Objective-C у користуванні Swift з какао та Objective-C .
Примітка: Перед Swift 2.2, Selector
відповідно до StringLiteralConvertible
, таким чином, ви можете знайти старий код, де голі рядки передаються API, що приймають селектори. Вам потрібно запустити "Перетворити на поточний синтаксис Swift" у Xcode, щоб отримати тих, хто використовує #selector
.
selector: test()
зателефонував биtest
і передав би це значення поверненняselector
аргументу.