What is difference between self.timer = nil vs [self.timer invalidate] in iOS?

前端 未结 3 1602
故里飘歌
故里飘歌 2020-12-10 14:48

Can anyone explain me self.timer=nil vs [self.timer invalidate]?

What exactly happens at the memory location of self.timer?

相关标签:
3条回答
  • 2020-12-10 14:51

    First of all, invalidate is a method of NSTimer class which can use to stop currently running timer. Where when you assign nil to any object then, in an ARC environment the variable will release the object.

    Its important to stop running timer when you don't longer need, so we write [timer invalidate] and then we write timer = nil; to make sure it'll loose its address from memory and later time you can recreate the timer.

    0 讨论(0)
  • 2020-12-10 15:03

    Once you have no need to run timer, invalidate timer object, after that no need to nullify its reference.

    This is what Apple documentation says: NSTimer

    Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A non-repeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate method. Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes the timer (and the strong reference it had to the timer), either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.

    0 讨论(0)
  • 2020-12-10 15:04

    There is a key difference not mentioned in the other answers.

    To test this drop the following code in Playground.

    1st Attempt:

    import Foundation
    import PlaygroundSupport
    
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    class Person{
        var age = 0
        lazy var timer: Timer? = {
            let _timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
            return _timer
        }()
    
        init(age: Int) {
            self.age = age
        }
    
        @objc func fireTimer(){
            age += 1
            print("age: \(age)")
        }
    
        deinit {
            print("person was deallocated")
        }
    }
    
    // attempt:
    var person : Person? = Person(age: 0)
    let _ = person?.timer
    person = nil
    

    So let me ask you a question. At the last line of the code I just set person to nil. That means the person object is deallocated and all its properties are set to nil and removed from memory. Right?

    An object is deallocated as long as no other object is holding a strong a reference to it. In our case the timer is still holding a strong reference to person, because the run-loop has a strong reference to the timer§ hence the person object will not get deallocated.

    The result of the above code is that it still continues to execute! Let's fix it.


    2nd Attempt:

    Let's set the timer to nil. This should remove the strong reference of timer pointing to person.

    var person : Person? = Person(age: 0)
    let _ = person?.timer
    person?.timer = nil
    person = nil
    

    WRONG! We only removed our pointer to the timer. Yet the result of the above code is just like our initial attempt. It still continues to execute...because the run loop is still targeting/referencing self.


    So what do we need to do?

    Glad you asked. We must invalidate the timer!

    3rd Attempt:

    var person : Person? = Person(age: 0)
    let _ = person?.timer
    
    person?.timer = nil
    person?.timer?.invalidate()
    person = nil
    

    This looks better, but it's still wrong. Can you guess why?

    I'll give you a hint. See code below

    0 讨论(0)
提交回复
热议问题