Làm điều gì đó mỗi x phút trong Swift


113

Làm thế nào tôi có thể chạy một chức năng mỗi phút? Trong JavaScript, tôi có thể làm điều gì đó như setInterval, có thứ gì đó tương tự tồn tại trong Swift không?

Đầu ra mong muốn:

Xin chào thế giới mỗi phút một lần ...


Cập nhật cho Swift 2: Bộ hẹn giờ Swift
JamesG

Câu trả lời:


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

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

Nhớ nhập Foundation.

Swift 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
nhưng nếu bạn muốn chuyển đổi giữa các chế độ xem thì sao? Mã sẽ dừng lại phải không?
Cing

5
@Cing phụ thuộc vào những gì tự đề cập đến.
Antzi,

1
Đừng quên rằng NSTimergiữ lại mục tiêu của nó, vì vậy, với thiết lập này, nếu helloWorldTimerlà một thuộc tính trên, selfbạn đã có cho mình một chu kỳ lưu giữ, nơi selfgiữ lại helloWorldTimerhelloWorldTimergiữ lại self.
MANIAK_dobrii

@MANIAK_dobrii Bạn cũng có thể nhận xét về cách phá vỡ chu kỳ lưu giữ? Nếu VC bị loại bỏ nhưng bộ đếm thời gian không bị hủy bỏ, chu kỳ vẫn còn nguyên vẹn? Vì vậy, bạn phải vừa hủy hẹn giờ và sau đó bỏ qua xem để phá vỡ chu kỳ?
Dave G

1
Đối với Swift 4, phương thức mà bạn muốn lấy bộ chọn phải được hiển thị với Objective-C, do đó, thuộc tính @objc phải được thêm vào khai báo phương thức. ví dụ: `` @objc func sayHello () {} `` '
Shamim Hossain

136

Nếu nhắm mục tiêu phiên bản iOS 10 trở lên, bạn có thể sử dụng kết xuất dựa trên khối Timer, giúp đơn giản hóa các chu kỳ tham chiếu mạnh tiềm năng, ví dụ:

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

Mặc dù Timernói chung là tốt nhất, vì lợi ích của sự hoàn chỉnh, tôi nên lưu ý rằng bạn cũng có thể sử dụng bộ đếm thời gian gửi, rất hữu ích để lập lịch bộ hẹn giờ trên các luồng nền. Với bộ hẹn giờ gửi, vì chúng dựa trên khối, nên nó tránh được một số thách thức về chu trình tham chiếu mạnh mẽ với mẫu target/ cũ , miễn là bạn sử dụng tham chiếu.selectorTimerweak

Vì thế:

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

Để biết thêm thông tin, hãy xem phần Tạo hẹn giờ của Ví dụ về Nguồn Công văn trong phần Nguồn Công văn của Hướng dẫn Lập trình Đồng thời.


Đối với Swift 2, hãy xem bản sửa đổi trước của câu trả lời này .


bạn sẽ làm cách nào để có bộ đếm thời gian thực thi một lần trong cách tiếp cận này?
Juan Boero

@JuanPabloBoero - Tôi sẽ không sử dụng cách tiếp cận này cho tình huống cháy một lần. Bạn sẽ sử dụng dispatch_after. Hoặc một không lặp lại NSTimer.
Rob

@Rob Tôi có một nhãn bộ đếm trên màn hình được cập nhật mỗi giây từ một chức năng và tôi đang sử dụng mã trên để tăng bộ đếm của mình và cập nhật văn bản nhãn. Tuy nhiên, vấn đề tôi đang gặp phải là biến bộ đếm của tôi được cập nhật mỗi giây, nhưng màn hình không hiển thị giá trị bộ đếm mới nhất trong UILabel của tôi.
Nagendra Rao

Rao, ở trên hữu ích để thực hiện một số hành động trên một chuỗi nền. Nhưng cập nhật giao diện người dùng của bạn phải xảy ra trên hàng đợi chính. Vì vậy, hãy lên lịch điều này trên chuỗi chính hoặc ít nhất gửi các bản cập nhật giao diện người dùng trở lại chuỗi chính.
Rob

1
Câu hỏi này không thuộc về đây trong các bình luận cho câu trả lời này. Xóa nhận xét của bạn ở trên và đăng câu hỏi của riêng bạn. Tuy nhiên, tóm lại, bạn thường không thực sự giữ cho ứng dụng chạy trong nền mà chỉ làm cho ứng dụng trông như cũ. Xem stackoverflow.com/a/31642036/1271826 .
Rob

17

Đây là bản cập nhật cho NSTimercâu trả lời, dành cho Swift 3 ( NSTimerđã được đổi tên thành Timer) bằng cách sử dụng một hàm đóng thay vì một hàm được đặt tên:

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

6
đó chỉ dành cho iOS 10.
Glenn

xin vui lòng không bài ios 10+ giải pháp
Numan Karaaslan

13

Nếu bạn có thể cho phép một thời gian trôi qua, đây là một giải pháp đơn giản thực hiện một số mã mỗi phút:

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

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

Chỉ cần chạy executeRepeatedly()một lần và nó sẽ được thực thi mỗi phút. Việc thực thi dừng lại khi đối tượng sở hữu ( self) được giải phóng. Bạn cũng có thể sử dụng một cờ để chỉ ra rằng việc thực thi phải dừng lại.


Quyết định này thuận tiện hơn là có tất cả những bộ hẹn giờ này, thêm chúng vào các vòng chạy, làm mất hiệu lực của chúng… Thật tuyệt!
julia_v

giải pháp này có vẻ hợp lệ nhưng nó đang tạo ra một chu kỳ lưu giữ khi tôi đang sử dụng nó với cuộc gọi dịch vụ web của mình ...
jayant rawat

11

Bạn có thể sử dụng Timer(nhanh chóng 3)

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

Trong selector (), bạn nhập tên hàm của mình


Làm thế nào bạn có thể làm điều đó trong nhanh chóng 3?
yếm vào

1
sử dụng Timer... NSTimerđã được đổi tên
Joseph

7

Trong 3.0 nhanh chóng, GCD đã được cấu trúc lại:

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

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

Điều này đặc biệt hữu ích khi bạn cần gửi trên một Hàng đợi cụ thể. Ngoài ra, nếu bạn định sử dụng tính năng này để cập nhật giao diện người dùng, tôi khuyên bạn nên xem xét CADisplayLinkvì nó được đồng bộ hóa với tốc độ làm mới GPU.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.