#swift #nstimer
#swift #nstimer
Вопрос:
В этом сценарии timerFunc() никогда не вызывается. Чего мне не хватает?
class AppDelegate: NSObject, NSApplicationDelegate {
var myTimer: NSTimer? = nil
func timerFunc() {
println("timerFunc()")
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
}
}
Комментарии:
1. Если вы используете таймер
init
, вы должныYou must add the new timer to a run loop, using addTimer:forMode:
. Это второе предложение в описании документа. В противном случае используйтеscheduledTimerWithTimeInterval
, вероятно, то, что вы искали.2. Почему голоса против, учитывая, что несколько человек дали разные ответы?
3. Не могу сказать вам наверняка, но, вероятно, это потому, что ответ нетрудно найти самостоятельно, как я указывал ранее. Это прямо в документах. Если бы вы
option
нажали на свой метод, вы бы нашли решение в течение 5 секунд и даже не выходили из Xcode.
Ответ №1:
Вы можете создать таймер по расписанию, который автоматически добавляется в цикл выполнения и начинает запускаться:
Swift 2
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerDidFire:", userInfo: userInfo, repeats: true)
Swift 3, 4, 5
Timer.scheduledTimer(withTimeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: userInfo, repeats: true)
Или вы можете сохранить свой текущий код и добавить таймер в цикл выполнения, когда будете готовы к этому:
Swift 2
let myTimer = NSTimer(timeInterval: 0.5, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(myTimer, forMode: NSRunLoopCommonModes)
Swift 3, 4, 5
let myTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(myTimer, forMode: RunLoop.Mode.common)
Комментарии:
1. Как приостановить и возобновить этот таймер?
2. MyTimer.invalidate() отменяет его. Кроме этого, NSTimer не поддерживает никаких функций приостановки / возобновления. Вы могли бы представить себе простой подкласс, который добавляет эту поддержку, (1) отмечая время начала, (2) отмечая время паузы и (3) при возобновлении выполнения на t2-t1 короче, чем наш исходный timeInterval.
3. Синтаксис Sift 3.0 для цикла выполнения: RunLoop.current.add(MyTimer, forMode: RunLoopMode.commonModes)
4. Swift3
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
Ответ №2:
я использую аналогичный подход к Luke. Только предостережение для людей, которые являются пуристами «частных методов»:
НЕ делайте обратный вызов приватным в Swift.
Если вы напишете:
private func timerCallBack(timer: NSTimer){
..
вы получите:
TimerCallback:]: нераспознанный селектор, отправленный экземпляру… Завершение работы приложения из-за неперехваченного исключения ‘NSInvalidArgumentException’
Комментарии:
1. Большое вам спасибо за это!
2. просто добавьте
@objc
в частный метод, и он также будет работать:@objc private func timerCallBack(timer: NSTimer){
3. Предложенный ответ @Lensflare правильный, вам просто нужен
@objc
декоратор метода.4. да, я знаю «@objc», но мы говорим о пуристе «swift»… поэтому избегайте всех старых сделок с objc. (Мне это тоже понравилось ..)
Ответ №3:
NSTimer не планируются автоматически, если вы не используете NSTimer.scheduledTimerWithTimeInterval
:
myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc", userInfo: nil, repeats: true)
Ответ №4:
Как указали Drewag и Ryan, вам необходимо создать таймер по расписанию (или запланировать его самостоятельно) Проще всего создать его запланированным уже с:
myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc:", userInfo: nil, repeats: true)
Вам также необходимо изменить определение timerFunc (и связанного с ним селектора), чтобы принимать аргумент и заканчиваться символом ‘:’
func timerFunc(timer:NSTimer!) {
...
}
Комментарии:
1. Вам не нужно определять его, чтобы принимать аргумент. В любом случае все в порядке.
2. @drewag хорошо, я только что вспомнил некоторое обсуждение SO о том, что оно не работает без ‘:’, поэтому я попробовал его на игровой площадке. Вам нужно ‘:’, если вы (правильно) определяете обратный вызов как принимающий аргумент. «Правильно», потому что в
NSTimer
документации говорится, что обратный вызов должен принимать один аргумент. «Селектор должен иметь следующую подпись: timerFireMethod: (включая двоеточие, чтобы указать, что метод принимает аргумент)». Кажется, что вы можете обойтись без : или аргумента, но я бы все же сказал, что мой ответ правильный, основанный на документации 🙂3. аргументы также являются необязательными в IBActions и в любом другом месте, где я когда-либо определял селектор (по крайней мере, с одним аргументом). Это просто природа среды выполнения. Тот факт, что он пропустил аргумент, не имеет ничего общего с тем, почему он не вызывается. В худшем случае это выдаст исключение о том, что селектор не найден, а не сбой без сбоев.
Ответ №5:
Для Swift 3
var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.updateTimer), userInfo: nil, repeats: true);
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
Комментарии:
1. Использование
scheduledTimer()
метода автоматически добавит ваш таймер в цикл выполнения — вам не нужно добавлять его вручную.
Ответ №6:
Синтаксис Swift 3.0 для выполнения цикла:
RunLoop.current.add(myTimer, forMode: .commonModes)
Ответ №7:
Это фрагмент кода, который демонстрирует, как вызывать функцию (с задержкой) с параметром и без него.
Используйте это в новом проекте в xCode (singleViewApplication) и поместите код в стандартный ViewController:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("delayedFunctionWithoutParameter:"), userInfo: nil, repeats: false)
let myParameter = "ParameterStringOrAnyOtherObject"
NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: Selector("delayedFunctionWithParameter:"), userInfo: myParameter, repeats: false)
}
// SIMPLE TIMER - Delayed Function Call
func delayedFunctionWithoutParameter(timer : NSTimer) {
print("This is a simple function beeing called without a parameter passed")
timer.invalidate()
}
// ADVANCED TIMER - Delayed Function Call with a Parameter
func delayedFunctionWithParameter(timer : NSTimer) {
// check, wether a valid Object did come over
if let myUserInfo: AnyObject = timer.userInfo {
// alternatively, assuming it is a String for sure coming over
// if let myUserInfo: String = timer.userInfo as? String {
// assuming it is a string comming over
print("This is an advanced function beeing called with a parameter (in this case: (myUserInfo)) passed")
}
timer.invalidate()
}
}
Обратите внимание, что в любом случае вы должны реализовать отложенную функцию с параметром (timer : NSTimer), чтобы иметь возможность аннулировать (завершить, завершить) таймер. И с помощью пароля «timer» у вас также есть доступ к userInfo (и там вы можете поместить любой объект, а не только строковые объекты, а также типы коллекций, такие как массивы и словари).
В оригинальной документации Apple говорится, что «» -> Таймер передает себя в качестве аргумента, поэтому метод будет использовать следующий шаблон: — (void)timerFireMethod:(NSTimer *) таймер Читать полностью -> здесь
Ответ №8:
Поскольку этот поток заставил меня попытаться самостоятельно установить таймер на цикл выполнения (что решило мою проблему), я также публикую свой конкретный случай — кто знает, может быть, это кому-то поможет. Мой таймер создается во время запуска приложения и инициализации всех объектов. Моя проблема заключалась в том, что, хотя он и планировал таймер, он все равно никогда не срабатывал. Я предполагаю, что это было так, потому scheduledTimerWithTimeInterval
что во время запуска приложения таймер был установлен на другой цикл выполнения. Если я просто инициализирую таймер, а затем использую NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)
вместо него, он работает нормально.
Ответ №9:
С swift3 вы можете запустить его с,
var timer: Timer?
func startTimer() {
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
}
}
func stopTimer() {
if timer != nil {
timer?.invalidate()
timer = nil
}
}
func loop() {
//do something
}
Ответ №10:
Чтобы сделать это с помощью метода, предложенного OP, вам нужно добавить его в цикл выполнения:
myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)
В документации также говорится, что цель должна принимать аргумент, но она работает без него.
func timerFireMethod(timer: NSTimer) { }