Как использовать @selector() в Swift для NSTimer и других методов?
Проблема с @selector в Swift и её решение
Здравствуйте! Ваша ошибка связана с тем, что Swift — язык, который по умолчанию не экспортирует методы в Objective-C runtime. Селекторы — это механизм из Objective-C, поэтому для их использования в Swift нужно явно указать, что метод доступен для Objective-C. Это делается с помощью атрибута @objc.
Вот исправленный код для вашего таймера:
import UIKit
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Теперь селектор работает корректно
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}
@objc func updateTimer() {
print("Таймер сработал!")
}
}
Ключевые моменты
- Атрибут @objc — обязателен для любого метода, который вы хотите использовать с селектором. Он сообщает компилятору, что метод должен быть виден из Objective-C runtime.
- Синтаксис #selector — в Swift используется
#selector(имяМетода)вместо@selector(). Это более безопасно, так как компилятор проверяет, существует ли метод. - Наследование от NSObject — если ваш класс наследуется от
NSObject(например,UIViewControllerнаследует), то все методы с@objcавтоматически становятся доступными. Для чистых Swift-классов нужно явно добавлять@objcили наследовать отNSObject.
Работа с методами, принимающими параметры
Если ваш метод принимает параметры, например, сам таймер, то селектор записывается так:
@objc func updateTimer(timer: Timer) {
print("Таймер сработал! \(timer)")
}
// В viewDidLoad:
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer(timer:)), userInfo: nil, repeats: true)
Обратите внимание на #selector(updateTimer(timer:)) — мы указываем имя метода с параметром. Это обязательно, если метод принимает аргументы.
Альтернативный синтаксис для селекторов с несколькими параметрами
Если метод принимает несколько параметров, например:
@objc func handleTap(sender: UITapGestureRecognizer, event: UIEvent) {
// обработка
}
Тогда селектор будет:
#selector(handleTap(sender:event:))
Когда @objc не нужен
В некоторых случаях @objc добавляется автоматически:
- Если метод переопределяет метод из Objective-C (например,
viewDidLoad). - Если метод реализует протокол Objective-C (например,
UITableViewDataSource). - Если класс наследуется от
NSObjectи метод помечен@IBActionили@IBOutlet.
В вашем случае с таймером, просто добавьте @objc перед методом, и всё заработает. Удачи в изучении Swift!
Использование замыканий вместо селекторов
В современном Swift (начиная с версии 3.0) можно избежать использования селекторов, если вы работаете с таймерами. Вместо метода с @objc вы можете передать замыкание (closure) непосредственно в таймер. Это более чистый и Swift-стильный подход, который не требует взаимодействия с Objective-C runtime.
Вот как это выглядит:
import UIKit
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
// Используем weak self для предотвращения утечек памяти
self?.updateTimer(timer: timer)
}
}
func updateTimer(timer: Timer) {
print("Таймер сработал! \(timer)")
}
}
Преимущества этого подхода:
- Не нужно добавлять
@objcк методам. - Нет риска опечаток в имени селектора — компилятор проверяет типы.
- Лучшая производительность, так как не задействуется динамическая диспетчеризация Objective-C.
- Более читаемый код для разработчиков, знакомых с замыканиями.
Обратите внимание на [weak self] — это важно для предотвращения циклических ссылок, так как таймер удерживает замыкание, а замыкание удерживает self. Если ваш метод не использует self, можно опустить weak self.
Этот подход работает для всех версий Swift начиная с 3.0. Если вам нужно поддерживать более старые версии (Swift 2.x), то придётся использовать селекторы с @objc.
Ответов пока нет.
Войдите чтобы оставить ответ