在Swift上设置Timer

时间:2018-02-08 13:44:12

标签: ios swift swift3 timer

我试图重复执行函数pepe(),我没有错误,但它不起作用。

这是我的代码:

public class MyClass {
    var timer = Timer()
    @objc func pepe() -> String {
        let hola = "hola"
        return hola
    }
    func startTimer(){
         let seconds = 1.0
        timer = Timer.scheduledTimer(timeInterval: seconds, target: ().self, selector: #selector(pepe), userInfo: nil, repeats: false)

    }
    func stopTimer() {

        timer.invalidate()

    }

    init() {
        self.startTimer()
        self.stopTimer()
    }
}
var pepe = MyClass()
pepe.stopTimer()
pepe.startTimer()

3 个答案:

答案 0 :(得分:8)

我建议:

  1. 不要实例化空Timer。考虑:

    var timer = Timer()
    

    即创建一个空白计时器实例。我们不想这样做。你应该使用:

    weak var timer: Timer?
    

    实现了一些目标:

    • Timer?语法表示它是“可选的”,您将在稍后实例化其值。

    • 安排Timer后,runloop会对其进行强有力的引用。因此,与大多数其他对象不同,您个人不需要对预定的计时器进行强有力的引用。当计时器失效时,您可能希望timer变量自动设置为nil。因此,weak限定符表示当计时器失效时,timer变量将自动设置为nil

  2. pepe方法签名不太正确:

    • 它不应该返回任何内容;

    • 你应该给它Timer参数。进入是一个很好的习惯。您可能不需要它,但它使方法的意图更加清晰,您最终可能会发现使用Timer参数很有用;以及

    • 我可能会给它一个更具描述性的名称,以避免任何歧义;我倾向于使用像timerHandler这样的名字。

  3. init开始和停止计时器是没有意义的。

  4. ().self中对target的引用应为self

  5. 在您的操场上,您正在停止计时器(尚未安排的计时器),然后启动计时器。

    您可能还希望在一段时间后停止播放,这样您就有机会看到Timer正在运行。

  6. 但是,作为一般规则,在编写启动计时器的方法时,谨慎的做法是确保您没有(意外地)已经启动它。如果你不这样做,并且意外地两次调用startTimer,你最终可能会同时进行多个定时器(最糟糕的是,丢失了对早期定时器的引用)。一个常见的解决方案是典型的解决方案是查看是否已有计时器,如果是,则在创建下一个计时器之前使其无效。使用可选的链接模式很容易实现:

    func startTimer() {
        timer?.invalidate()   // stops previous timer, if any
    
        // now proceed with scheduling of new timer
    }
    
  7. 因此:

    import UIKit
    
    // if doing this in playground, include the following two lines
    
    import PlaygroundSupport
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    // MyClass
    
    public class MyClass {
        weak var timer: Timer?
    
        @objc func timerHandler(_ timer: Timer) {
            let hola = "hola"
            print(">>>> \(hola)")
        }
    
        func startTimer() {
            timer?.invalidate()   // stops previous timer, if any
    
            let seconds = 1.0
            timer = Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(timerHandler(_:)), userInfo: nil, repeats: true)
        }
    
        func stopTimer() {
            timer?.invalidate()
        }
    }
    
    var object = MyClass()
    object.startTimer()
    
    // stop it 10 seconds later
    DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
        object.stopTimer()
    }
    

    但应该认识到,您最终可能会得到类似于强参考周期的东西:

    • runloop强烈引用预定的计时器;
    • 基于选择器的计时器保留对其target;
    • 的强引用
    • MyClasstarget)可能是最终使该计时器无效的原因。

    因此,在MyClass无效之前,无法取消分配Timer。而且,就目前情况而言,您不能只invalidate Timer deinit MyClass中的deinit,因为MyClass在计时器到来之前不会被调用无效。

    实际效果是,如果您将此MyClass作为视图控制器的属性并启动计时器然后关闭视图控制器,则计时器将继续运行[weak self]不会被解除分配。

    要解决此问题,您可能希望使用基于闭包的计时器和MyClass引用,从而消除计时器与MyClass之间的强引用。然后,您还可以在取消分配public class MyClass { weak var timer: Timer? deinit { timer?.invalidate() } func timerHandler(_ timer: Timer) { let hola = "hola" print(">>>> \(hola)") } func startTimer() { timer?.invalidate() // stops previous timer, if any let seconds = 1.0 timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: true) { [weak self] timer in self?.timerHandler(timer) } } func stopTimer() { timer?.invalidate() } } 时自动使计时器失效:

    <div id="div">
    
            <!-- acuity -->
    
            <div id="booking-div">
    
                <div id="booking-left"></div>
    
                <div id="booking-right">
                    <a><img id="close-image" src="{% static "img/close-button.svg" %}" alt="My image"></a>
                    {% for BarberProfile in queryset_list %}
                        <iframe class="iframe" id="{{BarberProfile.id}}-iframe" src="" frameBorder="0"></iframe><script src="https://d3gxy7nm8y4yjr.cloudfront.net/js/embed.js" type="text/javascript"></script>
                    {% endfor %}
                </div>
    
            </div>
    
            <!-- acuity -->
    
            <!-- profile -->
    
            {% for BarberProfile in queryset_list %}
    
            <a class="profile-div" id="{{BarberProfile.id}}" target="{{BarberProfile.booking_link}}" onClick="toggle_content(this.id)">
    
                <div id="image" style="background-image: url({{BarberProfile.image.url}})">
                    <div id="image-structure"></div>
                </div>
    
                <h4 id="name"> {{BarberProfile.first_name}} </h4>
                <p id="bio"> {{BarberProfile.bio}} </p>
                <img id="experience-image" src="{% static "img/experience.svg" %}" alt=""><p id="experience">{{BarberProfile.years_of_experience}} experience</p>
                <img id="ideal-image" src="{% static "img/speciality.svg" %}" alt=""><p id="ideal">Ideal for {{BarberProfile.ideal_for}} </p>
    
            </a>
    
            {% endfor %}
    
            <!-- profile -->
    
    </div>
    

答案 1 :(得分:0)

设置repeats = true,将其设为self而不是().self,我尝试了这个并且它可以正常工作,您也错误地尝试在init中启动和停止计时器

public class MyClass {
    var timer = Timer()
    @objc func pepe()  {
        let hola = "hola"
        print("\(hola)")
    }
    func startTimer(){
        let seconds = 1.0
        timer = Timer.scheduledTimer(timeInterval: seconds, target:self, selector: #selector(pepe), userInfo: nil, repeats: true)

    }
    func stopTimer() {

        timer.invalidate()

    }

    init() {

    }
}

答案 2 :(得分:0)

尝试

 import UIKit
import AVFoundation

class PeopleViewController: UIViewController {

    var countdownTimer: Timer!
    var totalTime = 0

    let timerLbl: UILabel = {

        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.font = .boldSystemFont(ofSize: 22)
        lbl.text = "HH:MM:SS"
        return lbl
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        totalTime = defaults.integer(forKey: "time")
        view.backgroundColor = .white
        view.addSubview(timerLbl)

        NSLayoutConstraint.activate([

            timerLbl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            timerLbl.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            timerLbl.heightAnchor.constraint(equalToConstant: 30)
            ])
     //   startTimer()

//        doBackgroundTask()

        UIApplication.shared.runInBackground({
            self.startTimer()
        }) {
            // task after expiration.
        }
//        DispatchQueue.main.async(execute: {
//
//            NotificationCenter.default.addObserver(self, selector:#selector(self.startTimer), name:UIApplication.willEnterForegroundNotification, object: nil)
//
//        })
    }



//    deinit {
//        NotificationCenter.default.removeObserver(self)
//    }

//    var backgroundUpdateTask: UIBackgroundTaskIdentifier?
//
//    func doBackgroundTask() {
//
//        DispatchQueue.main.async {
//
//            self.beginBackgroundUpdateTask()
//
//            self.StartupdateLocation()
//
//            self.endBackgroundUpdateTask()
//
//        }
//    }

//    func beginBackgroundUpdateTask() {
//        self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
//            self.endBackgroundUpdateTask()
//        })
//    }
//
//    func endBackgroundUpdateTask() {
//        if let bgTask = self.backgroundUpdateTask {
//            UIApplication.shared.endBackgroundTask(bgTask)
//            self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
//        }
//    }
//
//    func StartupdateLocation() {
//        startTimer()
//    }

    let defaults = UserDefaults.standard
    @objc func startTimer() {
        print(countdownTimer)
        countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
    }

    @objc func updateTime() {

        print(timerLbl.text as Any)
        defaults.set(totalTime, forKey: "time")
        let updateTime = defaults.integer(forKey: "time")
        timerLbl.text = "\(timeFormatted(updateTime))"

        totalTime += 1

        defaults.set(totalTime, forKey: "time")
//        if totalTime != 0 {
//            totalTime += 1
//        } else {
//            endTimer()
//        }


    }

    func endTimer() {
        countdownTimer.invalidate()
    }

    func timeFormatted(_ totalSeconds: Int) -> String {
        let seconds: Int = totalSeconds % 60
        let minutes: Int = (totalSeconds / 60) % 60
        let hours: Int = totalSeconds / 3600
        return String(format: "%02d:%02d:%02d", hours,minutes, seconds)
    }


}


extension UIApplication {
    /// Run a block in background after app resigns activity
    public func runInBackground(_ closure: @escaping () -> Void, expirationHandler: (() -> Void)? = nil) {
        DispatchQueue.main.async {
            let taskID: UIBackgroundTaskIdentifier
            if let expirationHandler = expirationHandler {
                taskID = self.beginBackgroundTask(expirationHandler: expirationHandler)
            } else {
                taskID = self.beginBackgroundTask(expirationHandler: { })
            }
            closure()
            self.endBackgroundTask(taskID)
        }
    }

}