Lakukan sesuatu setiap x menit di Swift

113

Bagaimana cara menjalankan suatu fungsi setiap menit? Di JavaScript saya bisa melakukan sesuatu seperti setInterval, apakah yang serupa ada di Swift?

Output yang diinginkan:

Halo Dunia sekali sebentar ...

JOSEFtw
sumber
Diperbarui untuk Swift 2: Swift Timer
JamesG

Jawaban:

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

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

Ingatlah untuk mengimpor Foundation.

Cepat 4:

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

 @objc func sayHello() 
 {
     NSLog("hello World")
 }
Unheilig
sumber
1
tetapi bagaimana jika Anda ingin beralih di antara tampilan? Kode akan berhenti kan?
Cing
5
@Cing bergantung pada apa yang dimaksud dengan diri sendiri.
Antzi
1
Jangan lupa bahwa NSTimermempertahankan targetnya, jadi, dengan penyiapan ini, jika helloWorldTimerproperti di selfAnda memiliki siklus penahan, di mana selfmempertahankan helloWorldTimerdan helloWorldTimermempertahankan self.
MANIAK_dobrii
@MANIAK_dobrii Dapatkah Anda juga mengomentari cara memutus siklus penahanan? Jika VC diberhentikan tetapi timer tidak dibatalkan, siklus masih berfungsi? Jadi Anda berdua harus membatalkan pengatur waktu dan kemudian menutup tampilan untuk memutus siklus?
Dave G
1
Untuk Swift 4, metode yang Anda inginkan untuk mendapatkan selektornya harus diekspos ke Objective-C, sehingga atribut @objc harus ditambahkan ke deklarasi metode. misalnya `` `` @objc func sayHello () {} ``
Shamim Hossain
136

Jika menargetkan iOS versi 10 dan yang lebih baru, Anda dapat menggunakan rendisi berbasis blok Timer, yang menyederhanakan potensi siklus referensi yang kuat, misalnya:

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

Meskipun Timerumumnya yang terbaik, demi kelengkapan, saya harus mencatat bahwa Anda juga dapat menggunakan pengatur waktu pengiriman, yang berguna untuk menjadwalkan pengatur waktu pada utas latar belakang. Dengan pengatur waktu pengiriman, karena berbasis blok, ini menghindari beberapa tantangan siklus referensi yang kuat dengan pola target/ lama , selama Anda menggunakan referensi.selectorTimerweak

Begitu:

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

Untuk informasi lebih lanjut, lihat bagian Membuat Timer dari Contoh Sumber Pengiriman di bagian Sumber Pengiriman dari Panduan Pemrograman Konkurensi.


Untuk Swift 2, lihat revisi sebelumnya dari jawaban ini .

rampok
sumber
bagaimana Anda akan menggunakan pengatur waktu hanya-eksekusi-sekali dalam pendekatan ini?
Juan Boero
@JuanPabloBoero - Saya tidak akan menggunakan pendekatan ini untuk situasi sekali tembak. Anda akan menggunakan dispatch_after. Atau tidak berulang NSTimer.
Rob
@Rob Saya memiliki label penghitung di layar yang diperbarui setiap detik dari suatu fungsi, dan saya menggunakan kode di atas untuk menaikkan penghitung saya dan memperbarui teks label. Tapi, masalah yang saya hadapi adalah variabel penghitung saya diperbarui setiap detik, tetapi layar tidak menunjukkan nilai penghitung terbaru di UILabel saya.
Nagendra Rao
Rao, di atas berguna untuk melakukan beberapa tindakan pada thread latar belakang. Namun pembaruan UI Anda harus dilakukan di antrean utama. Jadi, jadwalkan ini di thread utama atau setidaknya kirim update UI kembali ke thread utama.
Rob
1
Pertanyaan ini tidak termasuk di sini dalam komentar untuk jawaban ini. Hapus komentar Anda di atas dan posting pertanyaan Anda sendiri. Namun, singkatnya, Anda biasanya tidak benar-benar menjalankan aplikasi di latar belakang, tetapi hanya membuatnya terlihat seperti apa adanya. Lihat stackoverflow.com/a/31642036/1271826 .
Rob
17

Berikut pembaruan untuk NSTimerjawabannya, untuk Swift 3 (yang NSTimernamanya diganti menjadi Timer) menggunakan closure daripada fungsi bernama:

var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
    (_) in
    print("Hello world")
}
John T
sumber
6
itu hanya untuk iOS 10.
Glenn
tolong jangan memposting solusi ios 10+
Numan Karaaslan
13

Jika Anda dapat membiarkan beberapa waktu melayang, berikut adalah solusi sederhana yang mengeksekusi beberapa kode setiap menit:

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

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

Jalankan saja executeRepeatedly()sekali dan akan dijalankan setiap menit. Eksekusi berhenti ketika objek pemilik ( self) dilepaskan. Anda juga dapat menggunakan tanda untuk menunjukkan bahwa eksekusi harus dihentikan.

algrid.dll
sumber
Keputusan ini jauh lebih nyaman daripada menggunakan semua pengatur waktu ini, menambahkannya ke runloop, membatalkannya… Keren!
julia_v
ini tampaknya solusi yang valid tetapi ini menciptakan siklus
penahanan
11

Anda dapat menggunakan Timer(swift 3)

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

Di selector () Anda memasukkan nama fungsi Anda

Bas
sumber
Bagaimana Anda bisa melakukannya dengan cepat 3?
bibscy
1
gunakan Timer... NSTimertelah diganti namanya
Joseph
7

Di 3.0 cepat, GCD difaktor ulang:

let timer : DispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)

timer.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timer.setEventHandler
{
    NSLog("Hello World")
}
timer.resume()

Ini sangat berguna ketika Anda perlu mengirim pada Antrian tertentu. Juga, jika Anda berencana menggunakan ini untuk pembaruan antarmuka pengguna, saya sarankan untuk melihat CADisplayLinkkarena disinkronkan dengan kecepatan refresh GPU.

Bisa
sumber