Делайте что-нибудь каждые x минут в Swift
Как я могу запустить функцию каждую минуту? В JavaScript я могу сделать что-то вроде setInterval
Есть ли что-то подобное в Swift?
Требуемый выход:
Привет мир раз в минуту...
9 ответов
var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
func sayHello()
{
NSLog("hello World")
}
Не забудьте импортировать Фонд.
Свифт 4:
var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)
@objc func sayHello()
{
NSLog("hello World")
}
В Swift 3 вы можете создать Timer
, И если вы нацелены на iOS версии 10 и выше, вы можете использовать блочную передачу, которая упрощает потенциальные сильные циклы ссылок, например:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // just in case you had existing `Timer`, `invalidate` it before we lose our reference to it
timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
// do something here
}
}
func stopTimer() {
timer?.invalidate()
}
// if appropriate, make sure to stop your timer in `deinit`
deinit {
stopTimer()
}
В Swift 2 вы создаете NSTimer
, И если вы используете Swift 2, вы вполне можете использовать версию iOS до 10.0, и в этом случае вы должны использовать более старую версию target
/selector
шаблон:
weak var timer: NSTimer?
func startTimer() {
timer?.invalidate() // just in case you had existing `NSTimer`, `invalidate` it before we lose our reference to it
timer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
}
func handleTimer(timer: NSTimer) {
// do something here
}
func stopTimer() {
timer?.invalidate()
}
// because this old target/selector approach will keep a strong reference
// to the `target`, if you want the timer to stop when the view controller
// is dismissed, you can't stop the timer in `deinit`, but rather have to
// detect the dismissing of the view controller using other mechanisms. Commonly,
// we used to detect the view disappearing, like below:
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
stopTimer()
}
В то время как NSTimer
Как правило, лучше всего, для полноты, я должен отметить, что вы также можете использовать таймер диспетчеризации, который полезен для планирования таймеров в фоновых потоках. Таймеры диспетчеризации, поскольку они основаны на блоках, позволяют избежать некоторых серьезных проблем, связанных со старым циклом, со старыми target
/selector
образец NSTimer
, пока вы используете weak
Рекомендации.
Итак, в Swift 3:
var timer: DispatchSourceTimer?
func startTimer() {
let queue = DispatchQueue(label: "com.domain.app.timer") // you can also use `DispatchQueue.main`, if you want
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timer!.setEventHandler { [weak self] in
// do whatever you want here
}
timer!.resume()
}
func stopTimer() {
timer?.cancel()
timer = nil
}
deinit {
self.stopTimer()
}
В Swift 2:
var timer: dispatch_source_t?
func startTimer() {
let queue = dispatch_queue_create("com.domain.app.timer", nil) // again, you can use `dispatch_get_main_queue()` if you want to use the main queue
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
dispatch_source_set_timer(timer!, DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC, 1 * NSEC_PER_SEC) // every 60 seconds, with leeway of 1 second
dispatch_source_set_event_handler(timer!) { [weak self] in
// do whatever you want here
}
dispatch_resume(timer!)
}
func stopTimer() {
if let timer = timer {
dispatch_source_cancel(timer)
self.timer = nil
}
}
deinit {
self.stopTimer()
}
Для получения дополнительной информации см. Раздел " Создание таймера " в Примерах источника рассылки в разделе " Источники рассылки" в Руководстве по программированию параллелизма.
Если вы можете позволить некоторое время, вот простое решение, выполняющее некоторый код каждую минуту:
private func executeRepeatedly() {
// put your code here
DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
self?.executeRepeatedly()
}
}
Просто беги executeRepeatedly()
один раз, и это будет выполняться каждую минуту. Выполнение останавливается, когда владеющий объект (self
) выпущен. Вы также можете использовать флаг, чтобы указать, что выполнение должно быть остановлено.
Вот обновление к NSTimer
ответ, для Swift 3 (в котором NSTimer
был переименован в Timer
) используя замыкание, а не именованную функцию:
var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
(_) in
print("Hello world")
}
Ты можешь использовать NSTimer
var timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)
В selector() вы вводите имя вашей функции
В Swift 3.0 GCD подвергся рефакторингу:
let timer : DispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timer.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timer.setEventHandler
{
NSLog("Hello World")
}
timer.resume()
Это особенно полезно, когда вам нужно отправить в определенную очередь. Кроме того, если вы планируете использовать это для обновления пользовательского интерфейса, я предлагаю рассмотреть CADisplayLink
так как он синхронизирован с частотой обновления графического процессора.
Swift Concurrency (без таймера, без основы)
Вы можете выполнить цикл for так же просто, как:
for try await tick in Every(.seconds(3)) {
print(tick)
}
Реализовав эту простую асинхронную последовательность:
struct Every: AsyncSequence, AsyncIteratorProtocol {
typealias Element = Int
var duration: Duration
private(set) var tick = 0
init(_ duration: Duration) { self.duration = duration }
func makeAsyncIterator() -> Self { self }
mutating func next() async throws -> Element? {
try await Task.sleep(for: duration)
guard !Task.isCancelled else { return nil }
tick += 1
return tick
}
}
Отменяемый
Конечно, вы можете выполнить это асинхронно, обернув все это вTask
и отмените задачу в любое время, вот так:
let clock = Task {
for try await tick in Every(.seconds(3)) {
print(tick)
if tick == 3 { clock.cancel() } // Cancel the task after 3 ticks
}
}
️ SwiftUI
Выполнив это в.task
модификатор автоматически привяжет время жизни итерации к времени жизниView
. Не нужно вообще беспокоиться о недействительности:
.task {
do {
for try await tick in Every(.seconds(1)) { print(tick) }
} catch {
print(error)
}
}
Вот еще одна версия ответа algrid с простым способом остановить его
@objc func executeRepeatedly() {
print("--Do something on repeat--")
perform(#selector(executeRepeatedly), with: nil, afterDelay: 60.0)
}
Вот пример того, как запустить и остановить:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
executeRepeatedly() // start it
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NSObject.cancelPreviousPerformRequests(withTarget: self) // stop it
}
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true, block: myMethod)
func myMethod(_:Timer) {
...
}
или же
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
...
}
убедитесь, что таймер недействителен в какой-то момент, например, ваше время больше не видно, или ваш объект деист