Робіть щось кожні х хвилин у Свіфті


113

Як я можу запускати функцію щохвилини? У JavaScript я можу зробити щось на кшталт setInterval, чи є щось подібне у Swift?

Потрібний вихід:

Hello World раз на хвилину ...


Оновлено для Swift 2: Таймер Swift
JamesG

Відповіді:


171
var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)

func sayHello() 
{
    NSLog("hello World")
}

Не забудьте імпортувати Foundation.

Швидкий 4:

 var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)

 @objc func sayHello() 
 {
     NSLog("hello World")
 }

1
але що робити, якщо ви хочете перемикатися між переглядами? Код припинить правильно?
Cing

5
@Cing залежить від того, на що йдеться.
Анци

1
Не забувайте, що NSTimerзберігає свою ціль, тому при цьому налаштування, якщо helloWorldTimerце властивість, у selfвас є цикл збереження, де selfзберігається helloWorldTimerі helloWorldTimerзберігається self.
MANIAK_dobrii

@MANIAK_dobrii Чи можете ви також прокоментувати, як порушити цикл утримування? Якщо ВК відключено, але таймер не скасовано, цикл все ще знаходиться в такті? Отже, вам потрібно скасувати таймер, а потім відхилити подання, щоб перервати цикл?
Дейв Г

1
Для Swift 4, метод якого ви хочете отримати селектор, повинен бути підданий дії Objective-C, таким чином атрибут @objc повинен бути доданий до декларації методу. наприклад, `` `@objc func sayHello () {}` ``
Шамім Хоссайн

136

Якщо націлено на iOS версії 10 та новіших, ви можете використовувати блокове видання Timer, яке спрощує потенційні потужні еталонні цикли, наприклад:

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()
}

Хоча, Timerяк правило, найкраще, для повноти слід зазначити, що ви також можете використовувати таймер диспетчеризації, який корисний для таймерів планування фонових ниток. Що стосується таймерів диспетчеризації, оскільки вони засновані на блоці, це дозволяє уникнути деяких сильних проблем еталонного циклу зі старим target/ selectorшаблоном Timer, якщо ви використовуєте weakпосилання.

Так:

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!.schedule(deadline: .now(), repeating: .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 див. Попередню редакцію цієї відповіді .


як би ви перейшли до таймера, який виконується лише один раз у рамках цього підходу?
Хуан Боеро

@JuanPabloBoero - Я не використовував би такий підхід для ситуації, щойно мала пожежу. Ви б використали dispatch_after. Або неповторювані NSTimer.
Роб

@Rob У мене на екрані є ярлик лічильника, який оновлюється щосекунди від функції, і я використовую вищезгаданий код, щоб збільшити мій лічильник та оновити текст мітки. Але проблема, з якою я стикаюсь, полягає в тому, що моя змінна лічильник оновлюється щосекунди, але на екрані не відображається останнє значення лічильника в моєму UILabel.
Nagendra Rao

Рао, вищезгадане корисно для виконання деяких дій на фоновій нитці. Але оновлення вашого інтерфейсу повинні відбуватися в основній черзі. Отже, або заплануйте це на основній нитці, або принаймні відправте оновлення інтерфейсу користувача до основного потоку.
Роб

1
Це питання не належить тут у коментарях до цієї відповіді. Видаліть свої коментарі вище та опублікуйте власне запитання. Але, коротше кажучи, ви насправді не тримаєте додаток у фоновому режимі, а лише робите його таким, яким він був. Див. Stackoverflow.com/a/31642036/1271826 .
Роб

17

Ось оновлення NSTimerвідповіді для Swift 3 (в якому NSTimerбуло перейменовано на Timer) із застосуванням закриття, а не названої функції:

var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
    (_) in
    print("Hello world")
}

6
це лише для iOS 10
Гленн

будь ласка, не публікуйте рішення для ios 10+
Numan Karaaslan

13

Якщо ви можете дозволити деякий час дрейфу, ось просте рішення, виконуючи якийсь код щохвилини:

private func executeRepeatedly() {
    // put your code here

    DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
        self?.executeRepeatedly()
    }
}

Просто запустіть executeRepeatedly()один раз, і він буде виконуватися щохвилини. Виконання припиняється, коли selfзвільняється об'єкт, що володіє ( ). Ви також можете використовувати прапор, щоб вказати, що виконання повинно припинятися.


Це рішення є набагато зручнішим, ніж обхід усіх цих таймерів, додавання їх до запуску, скасування їх… Класно!
julia_v

це здається дійсним рішенням, але це створює цикл збереження, коли я використовую його під час свого дзвінка в веб-службу ...
jayant rawat

11

Ви можете використовувати Timer(швидкий 3)

var timer = Timer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)

У селекторі () ви вводите ім'я функції


Як це можна зробити в швидкому 3?
bibscy

1
використання Timer... NSTimerбуло перейменовано
Джозеф

7

У швидкому 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як це синхронізовано зі швидкістю оновлення GPU.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.