What is diffrence between self.timer = nil vs [sel

2019-02-16 13:58发布

问题:

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

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

In my code

self.timer=nil

doesn't stops the timer but

[self.timer invalidate]

stops the timer.

If you require my code I will update that too.

回答1:

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.



回答2:

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.



回答3:

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

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 keeping its pointer to it.


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