NSTimer & Selector - swift

So not sure what I'm doing wrong - been banging my head for a while and yes I have looked at quite a lot of other SO questions and tried lots of things but haven't been able to sort this out.
I'm trying to use NSTimer:
var x = NSTimer(timeInterval: 1, target: self, selector: "timeToMoveOn", userInfo: nil, repeats: false);
my selector function is:
func timeToMoveOn()
{
self.dismissViewControllerAnimated(false, completion: nil);
}
but what ever I do I keep getting this error 'Extra argument 'selector' in call.
I've tried things like scheduledTimerWithTimeInterval but get the same error. I've cleaned and rebuilt the project and just can't seem to get it to work.
What am I missing?
Thanks
Josh

okay, I solved my own problem...
I was accidentally declaring the var x timmer as a class member rather than within a function. Once I added it to viewDidLoad it worked fine. Interesting that selectors don't seem to work in class member scope.

Related

How can I replace #selector or #objc with any other codes?

I created a simple test to show what I am trying to do, sometimes we need to use some code that need to feed a selector and that selector need a function that have #objc, what I want and trying to do is deform the shape of code that I could bypass using #objc in front of function or more perfectly even deforming using selector also, basically I want avoid having selector or #objc in my codes, here is the example:
class MyTestAppClass {
#objc func test() {
print("Hello, world!")
}
}
let test = NSMenuItem(title: "Test", action: #selector(MyTestAppClass.test), keyEquivalent: "t")
as you can see the NSMenuItem needs a selector and I have to responding it with a func that has #objc, so all my goal is avoiding selector in first place and if that is not possible trying to avoid using #objc, How can i do this? Is there any way for bypass those? Also this question is not trying to learn about basic of selector or #objc, it simply tries to find hacks for reaching the goal of avoiding them. I want my code looks more swiftier after bypassing #selector or #objc. Is there any option or space in coding that allow us for some maneuvers?

Is There Any Reason to Use Selectors in Swift?

Specifically, when a task can be complemented with or without selectors, is there any (objective) reason to prefer one way over the other?
For instance, an NSTimer can run a method on an interval in two ways:
A) Using Selectors:
let timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction()), userInfo: nil, repeats: true)
dynamic func timerAction() {
print("foo")
}
B) Without Selectors:
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.timerAction()
}
func timerAction() {
print("foo")
}
Both versions are entirely functional, so is there any (objective) reason to prefer one over the other?
The target/selector design is baggage left over from the earlier Objective-C, pre-block days of the APIs.
If Apple were to redo all of those APIs today, the whole target/selector design would disappear and all be replaced with blocks/closures. This idea is supported by newer Apple APIs. Example - the old UIAlertView used a delegate. The newer UIAlertController only uses closures/blocks for the alert actions.
Using closures (Swift) or blocks (Objective-C) is much preferred over using the target/selector approach.
Here are problems with using target/selector:
The dreaded "unrecognized selector" error.
The need to create an extra, top-level method just for the selector.
The possibility of specifying the wrong parameters and data types on the selector method.
Losing all of the current context you have at the point you setup the object that needs the target/selector.
The opposite of all of those are the benefits of using the closure/block form.
It's possible, that over time, many of the delegate versions of APIs will become deprecated and replaced with closure/block versions. If you have the choice, I'd suggest using the closure/block form now to avoid the need to update later, not to mention it's just the simpler solution.

Object is nil only in dictionary

If I declare and set a variable at the top of a class, like
class Test {
var timer = NSTimer()
...
and test its validity in a function later, like if timer.valid {...}, no problem. But if I put it in a dictionary
var timers = ["first": NSTimer(), ...]
and test that with if timers["first"]!.valid {...}, I get an "unexpectedly found nil" fatal error at runtime. Why are these behaviors different, and how can I get the dictionary not to throw out my timer initialization? If this is a duplicate please point it out, I just don't really know what to search for. The Dictionary docs didn't shed any light, and I haven't tried it with anything but NSTimer.
This is a bug in Foundation and you should open a defect. NSTimer() is not a valid initializer and it should be marked as unavailable. A similar thing occurs with NSError by the way. Swift lets you construct them with NSError() even though they are then invalid and will crash if you try to use them.
The fact that you happen to get away with it, and it likely is returning "false" due to nil-messaging (which is how ObjC works), shouldn't be seen as "it should work." NSTimer() is invalid code.
Unlike int, string, and other data types, which in you initialize by saying int() or String(). You cannot simply do NSTimer(). It crashes when you try to do that, so you always get a nil. If you want a timer, try
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval, target: AnyObject, selector: Selector, userInfo: AnyObject?, repeats: Bool)
Just fill in the time interval, target, selector, user info, and repeats.
From the NSObject docs about the init() initializer:
Implemented by subclasses to initialize a new object (the receiver)
immediately after memory for it has been allocated.
Later:
An object isn’t ready to be used until it has been initialized.
According to the NSTimer docs, init() is not listed as a valid initializer for creating a timer.

NSTimer Confusion

I'm trying to have a label subtract every second (a countdown of sorts) and when I make a variable "timer" and set it to NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "countdown", userInfo: nil, repeats: true) it won't work. The countdown function that I'm trying to call subtracts from the label but when I try to put the "countdown" in the selector it says to replace it with #selector(Hard.countdown). All the tutorials on YouTube are different so I do not know what to do. Your help will be greatly appreciated.
Swift 2.2 added the new #selector syntax for safer selector. (We use pure string before). For NSTimer, you'll need to have a countdown method like the following:
func countdown(timer: NSTimer)
Don't forget the parameter, otherwise it won't work. And reference it as a selector like this: #selector(Hard.countdown(_:))

Deinit never called

I'm creating a ViewController object an pushing it to a navigation controller.
When the object is being popped from the stack - it is not being release and Deinit is not being called. What can be the reason for that?
Here's the code that pushes:
self.navigationController?.pushViewController(CustomViewController(), animated: true)
And here's the code that pops:
self.navigationController?.popViewControllerAnimated(true)
I had similar problem. I added empty deinit method to my class and added breakpoint:
deinit {
}
As result it's never called.
As soon as I add some code to the body it started working as expected:
deinit {
print("deinit called")
}
So be sure that your deinit method isn't empty.
PS. I am using Swift 2, Xcode 7.1
Do any of your classes, or properties they contain make a reference to the view controller you've popped?
If your UIViewController has created an instance of an object, which in turn makes a 'strong' reference to that view controller (e.g. a reference that's not explicitly declared 'weak' or 'unowned'), and your view controller keeps a strong reference to that object as well, neither will be deallocated. That's called a strong reference cycle, documented here (a must read for serious Swift developers):
The Swift Programming Language (Swift 3.0.1): Automatic Reference Counting
Closures are a more insidious case where you can get into trouble.
One thing you might try as an experiment is pushing the controller and popping it before you do anything in viewDidLoad or in the initialization, and see if the deinit method is being called. If it is, then you should be able to incrementally discover what you're doing that's leading to the symptom you're seeing.
Another thing that can thwart diagnosis (as other answers have pointed out), which I learned the hard way, is that the debugger breakpoint won't be taken for deinit() if the deinit method contains no executable statements, because the OS or compiler optimizes the deinit invocation away if it's empty, so at least put a print statement there if you want to verify deinit() is getting called.
I was using this tutorial Custom TabBar with a custom seque on the given page. The memory problem was due a subview which had a strong reference the the parent viewcontroller.
class WBMNewsFeedV: UIView
{
var parentVC:WBMHomeTabVC!
}
WBMNewsFeedV - subclass
parentVC:WBMHomeTabVC - parent class ViewController
I changed it to :
class WBMNewsFeedV: UIView
{
weak var parentVC:WBMHomeTabVC!
}
So, the strong reference was nested inside a subviews and not visible at first. Hope this helps anyone. After the change deinit was always called in
WBMHomeTabVC
I had the same issue when the NotificationCenter was holding a strong reference to the presented view controlled and thus it was never getting released. So I had to add [weak self] to the block like this:
(in the viewDidLoad)
NotificationCenter.default.addObserver(forName: .showFoo, object: nil,
queue: OperationQueue.main) { [weak self] (notification) in
if let foo = notification.userInfo?["foo"] as? Foo {
self?.afooButton!.setImage(UIImage(named: "foo"), for: .normal)
}
}
add some line code of in deinit. If You put breakpoint at Empty deinit ,compiler will not stop you there
put this:
deinit {
print("any thing")
}
It will work ;)
I had a timer in my view controller, running every minute to update a label. I put a call in deinit to invalidate the timer, which I'm pretty sure is what I've always done in Objective-C (in dealloc) and it's worked. But it seems a little different in Swift so I moved the time creation/invalidation code to viewWillAppear/viewWillDisappear (which makes more sense really) and everything seems fine now!
I just ran into a similar issue. My problem appears to have been caused by a strong reference to a control that had delegate assignment not specified as 'weak' with a class type protocol.
I had same issue what I found is I didn't make weak reference for my other class delegate
protocol SomeClassDelegate : AnyObject {
func someClassDelegateMethod(<param>)
}
class SomeClass: NSObject {
// Make delegate weak reference
weak var delegate:InfractionDataManagerDelegate? = nil
< some code >
}
now deinit is being called on my implementation class.
Just to add an edge case which I found very difficult to detect:
If you allocate any UnsafeMutablePointers and provide self (the UIViewController) as pointee inside of your view controller, make sure to call pointer.deinitialize(count:), not just pointer.deallocate().
Otherwise, the reference to self will remain and the UIViewController will not deinitialize.
I faced the same Problem. In my case a never ending cycle of UIView.animateWithDuration... holds the ViewController in Memory and blocks the call of deinit. If you use something like this you have to stop it first bevor you remove the ViewController. Hope that helps.
I had a similar issue: I had some UIViews as arranged subviews of a *stackview" contained in a scrollview in a detail view controller. It worked when I removed the UIViews from the stackview on back button tap (willMove(toParent parent: UIViewController?)). Another thing to check is when in your view controller you have something like: let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue()) (that self could in some cases prevent a detail view controller to be deallocated when you click Back and move to the master)
First of all make sure to define deinit
deinit {
print("OverlayView deinit")
}
Use Xcode's object graph to check number of instances being created and if they are not getting deallocated then they will keep growing.
I was creating property of another ViewController on top of the file so i move it and place it in the scope it was being used that solved my problem. And its deinit started calling.
Moreover i was using a uiview property to show a overlay that need to be accessed from my viewcontroller at some places i make it optional and set it nil when after calling removefromsuperview on it.
var overlay: OverlayView?
overlay = nil
If still dealloc not calling then there could be a retain cycle issue as described above in that case you will have to check another viewcontroller(B) too if its calling back this controller(A) then set one of them as a weak variable.
the most helping material that I could find in this problem is link
But for me the problem was typealias