This is how I declare property in my subclass of UIViewController:
private weak var timer: NSTimer?
This is what I do in viewDidLoad():
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer", userInfo: nil, repeats: true)
And this is my deinit:
deinit {
timer?.invalidate()
timer = nil
}
Deinit is not called because of NSTimer. Do not make funny telling that inside NSTimer there is a strong reference to my controller:) How can I workaround this?
I think that #i_am_jorf gave you a good direction. If you look at the documentation of invalidate method you will find a full explanation why it is acting like that
This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.
So yes, retain cycle created if invalidate not called in an appropriate moment
Related
I am declaring a function as such:
#objc func fetchDatabase(completion: ((Bool) -> Void)? = nil)
I'm allowing the completion to be nil so I can either call it as fetchDatabase() or as
fetchDatabase(completion: { (result) in
// Stuff in here
})
However, I am also trying to use this function in a #selector for a Timer. I am creating this timer using the following line:
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(fetchDatabase), userInfo: nil, repeats: true)
Without the completion, this runs fine. However, with the completion added, I get an EXC_BAD_ACCESS error whenever the timer is run. Some help with correctly constructing this selector would be greatly appreciated, if this is in fact the error.
The selector passed to the timer only allows one of two possible signatures
someFunction()
someFunction(someLabel someParameter: Timer)
You can't pass your fetchDatabase(completion:) function because it doesn't match either of the two legal selector signatures.
You need to pass a valid selector which in turn calls your fetchDatabase(completion:) function. For example:
#objc timerHandler() {
fetchDatabase(completion: { (result) in
// Stuff in here
})
}
use #selector(timerHandler) with your timer.
Default arguments get applied at the calling site, so you'll need to generate two separate methods (one of which calls the other):
func fetchDatabase() { fetchDatabase(callback:nil) }
func fetchDatabase(callback:()->()) {
...
}
Now your scheduledTimer call should work fine.
Does exist any solution to make reusable protocol extension for more classes with selectors which would point to itself?
For example I am trying to make extension TimerHelper which adds appropriate functions to work with NSTimer. I found this:
https://forums.developer.apple.com/thread/26983
https://forums.developer.apple.com/message/49465#49465
But solution seems a bit twisty...
What I am trying to make in code, which doesn't work of course, is something like this:
protocol TimerHelper {
var timer:NSTimer { get set }
}
extension TimerHelper {
func startTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: self.updateTimer(), userInfo: nil, repeats: true)
}
func updateTimer() {
print("Timer updated.")
}
}
class ViewController: UIViewController, TimerHelper {
var timer: NSTimer = NSTimer()
func start() {
startTimer()
}
}
Thanks
Never do:
... = NSTimer()
Instead create the variable as an optional. You want to invalidate and destroy the timer, and you never want a timer that hasn't been configured properly or invalidated.
Your extension is inappropriate because it deals with aspects not covered by the protocol itself. You should have 2 protocols, where the second protocol extends the first and is called something like TimerActivation. It defines the functions and the extension implements them.
This doesn't change what you need to do in the VC to use the timer, but it makes your type system clean, effective, reusable and extensible.
For the self referential part you need to look at #selector, I haven't tried it inside a protocol before, should be interesting...
It may be wiser to supply the selector, or an invocation, to the start function, because there is little point in a protocol extension implementing the selector when the protocol is so general. But, I suppose you may want to add other child protocols with extensions which provide other implementations so you can mixin functionality, interesting idea...
With a NSNotificationCenter block, I have to use [unowned self] to avoid a strong reference cycle:
NSNotificationCenter.defaultCenter()
.addObserverForName(UIApplicationWillEnterForegroundNotification,
object: nil,
queue: nil,
usingBlock: { [unowned self] (notification : NSNotification!) -> Void in
self.add(123)
})
However, in UIView.animateWithDuration, I do not have to use [unowned self]:
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.someOutlet.alpha = 1.0
self.someMethod()
})
What's the difference?
The only difference between the animation block and the notification center block is that the animation block is extremely short-lived—it is executed immediately and then released.
In both cases, the blocks will capture self; but only the NSNotificationCenter code is problematic because the notification center will hold a reference to the block indefinitely, since the notification can happen at any time.
Note that this is different than a circular reference. A circular reference is often created by an object holding a reference to a block that captures self, like this:
self.myBlock = {
self.doSomething()
}
That circular reference means that self will never be deallocated. The notification center isn't a circular reference (since self doesn't hold a reference to the notification center), it's just a regular reference that will be held until the observer is removed.
The animations: () -> Void and completion: ((Bool) -> Void)? blocks for a UIView Animation do not retain a reference to self. This previous post is very informative on the topic
Do we need to use __weak self inside UIAnimationBlocks in ARC?
The notification block (NSNotification) -> Void does retain a reference to self, by passing it in as unowned in your case, it should not increment the retain count. I try to ensure I use either unowned, or weak, on any reference I pass into the closure. There is a great post about this found here http://krakendev.io/blog/weak-and-unowned-references-in-swift
But, I highly recommend staying away from using Notifications, especially with blocks, as there maybe a bug pointed out by this article, which was shocking when I read it.
http://sealedabstract.com/code/nsnotificationcenter-with-blocks-considered-harmful/
I need to add an NSTimer in the applicationDidEnterBackground method; I tried adding
NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: Selector("test"), userInfo: nil, repeats: true)
But it's not working.
According to your comments, your declaration should look something like this:
NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: "test:", userInfo: nil, repeats: false)
Note two things:
You can pass a string literal to the selector parameter directly (no need for Selector() syntax), but this is of course optional.
The NSTimer expects the selector that will fire when it hits zero to accept a parameter (of type NSTimer) to which it will pass itself, so a colon (:) is needed at the end of the selector name to indicate that it will take a parameter.
The selector should have the following signature: timerFireMethod:
(including a colon to indicate that the method takes an argument). The
timer passes itself as the argument, thus the method would adopt the
following pattern: - (void)timerFireMethod:(NSTimer *)timer
From the NSTimer documentation.
Because of this, you'll also have to modify your function declaration a bit:
func test(timer: NSTimer) {
// Proceed to grab location from background
}
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.
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.
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 👇.
4th Attempt (correct)
var person : Person? = Person(age: 0)
let _ = person?.timer
person?.timer?.invalidate()
person?.timer = nil
person = nil
// person was deallocated
Our 4th attempt was just like our 3rd attempt, just that the sequence of code was different.
person?.timer?.invalidate() removes the run loop's strong reference
to its target, i.e. self, and now if a pointer to person is removed...our person object gets deallocated!
The attempt below is also correct:
5th Attempt (correct)
var person : Person? = Person(age: 0)
let _ = person?.timer
person?.timer?.invalidate()
person = nil
// person was deallocated
Notice that in our 5th attempt we didn't set the timer to nil. But Apple recommends that we do such:
Once invalidated, timer objects cannot be reused.
See Task Management - Timer
Setting it to nil is also an indicator that for other parts of code. It helps up so that we can check against it and if it wasn't nil then we'd know the timer is still valid and also to not have a meaningless object around.
After invalidating the timer you should assign nil to the variable
otherwise the variable is left pointing to a useless timer. Memory
management and ARC have nothing to do with why you should set it to
nil. After invalidating the timer, self.timer is now referencing a
useless timer. No further attempts should be made to use that value. Setting it to nil ensures that any further attempts to access
self.timer will result in nil
from rmaddy's comment above
That being said I think isValid is a more meaningful approach just as isEmpty is more meaningful and efficient than doing array.count == 0...
So why is 3rd attempt not correct?
Because we need a pointer to the timer so we can invalidate it. If we set that pointer to nil then we loose our pointer to it. We lose it while the run-loop has still maintained its pointer to it! So if we ever wanted to turn off the timer we should invalidate it BEFORE we lose our reference to it (ie before we set its pointer to nil) otherwise it becomes an abandoned memory (not leak).
Conclusion:
To get stop a timer correctly you must use invalidate. Do not nil the timer before you invalidate it.
After you've invalidated a timer, set it to nil so it doesn't get reused.
Calling invalidate will remove the run loop's pointer to self. Only then the object containing the timer will be released.
So how does this apply when I'm actually building an application?
If your viewController has person property and then your popped this viewController off your navigation stack then your viewController will get deallocated. In its deinit method you must invalidate the person's timer. Otherwise your person instance is kept in memory because of the run loop and its timer action will still want to execute! This can lead to a crash!
Correction:
Thanks to Rob's answer
If you're dealing with repeating [NS]Timers, don't try to invalidate them in dealloc of the owner of the [NS]Timer because the dealloc obviously will not be called until the strong reference cycle is resolved. In the case of a UIViewController, for example, you might do it in viewDidDisappear
That being said viewDidDisappear may not always be the correct place since viewDidDisappear also gets called if you just push a new viewController on top of it. You should basically do it from a point that it's no longer needed. You get the idea...
§: Because the run loop maintains the timer, from the perspective of
object lifetimes there’s typically no need to keep a reference to a
timer after you’ve scheduled it. (Because the timer is passed as an
argument when you specify its method as a selector, you can invalidate
a repeating timer when appropriate within that method.) In many
situations, however, you also want the option of invalidating the
timer—perhaps even before it starts. In this case, you do need to
keep a reference to the timer, so that you can stop it whenever
appropriate.
With all the credit going to my colleague Brandon:
Pro Tip:
Even if you don't have a repeating timer, the Runloop [as mentioned within the docs] will hold a strong reference to your target if you use the selector function, until it fires, after that it will release it.
However if you use the block based function then as long as you point weakly to self inside your block then the runloop will not retain self. However it will continue to execute, due to the lack of calling invalidate
If you don't use [weak self] then the block based will act just like the selector kind, that it will deallocate self after it has been fired.
Paste the following code in Playground and see the difference. The selector version will be deallocated after it fires. The block base will be deallocated upon deallocation. Basically the lifecycle of one is governed by the runloop while for the other it's governed by the object itself
#objc class MyClass: NSObject {
var timer: Timer?
func startSelectorTimer() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(MyClass.doThing), userInfo: nil, repeats: false)
}
func startBlockTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { [weak self] _ in
self?.doThing()
})
}
#objc func doThing() {
print("Ran timer")
}
deinit {
print("My Class deinited")
}
}
var mySelectorClass: MyClass? = MyClass()
mySelectorClass?.startSelectorTimer()
mySelectorClass = nil // Notice that MyClass.deinit is not called until after Ran Timer happens
print("Should have deinited Selector timer here")
RunLoop.current.run(until: Date().addingTimeInterval(7))
print("---- NEW TEST ----")
var myBlockClass: MyClass? = MyClass()
myBlockClass?.startBlockTimer()
myBlockClass = nil // Notice that MyClass.deinit IS called before the timer finishes. No need for invalidation
print("Should have deinited Block timer here")
RunLoop.current.run(until: Date().addingTimeInterval(7))
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.