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.
Related
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?
A lot of people when creating a Timer app or a StopWatch, or similar uses this function:
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true);
in order to update the timer using a user defined function updateTimer that gets called every timeInterval.
But my question is whether using such function could give a non accurate timer, since we call a function that is user defined and we don't count the overheads that there may be, because of our implementation. And if we have loops or other stuff going on, am I right that this isn't the best approach?
I'm wondering because in my case I would need to use this function to increment a variable timer, and also another function that computes the string of the timer to display every time the updateTimer gets called.
What would be the better and most accurate approach to implement a timer? Maybe using NSDate?
I'd like to show B UIView iff A UIView is visible. I used ReactiveCocoa 2 in objective-c and tried to find a similar way to observe isHidden property of UIView in ReactiveSwift. I'm still trying to learn the framework and its usage, but couldn't come up with a good solution. I'd appreciate if anyone can give me an advice.
Here's the KVO example from the ReactiveSwift readme:
// A producer that sends the current value of `keyPath`, followed by
// subsequent changes.
//
// Terminate the KVO observation if the lifetime of `self` ends.
let producer = object.reactive.values(forKeyPath: #keyPath(key))
.take(during: self.reactive.lifetime)
So in your case you can do something like this (haven't actually tried this code, but it should convey the idea):
viewA.reactive.values(forKeyPath: #keyPath(isHidden))
.take(during: self.reactive.lifetime)
.startWithValues { hidden in viewB.isHidden = hidden }
UPDATE:
I just noticed that ReactiveCocoa includes a binding target for UIView`s isHidden property, so you can actually simplify the above code to:
viewB.reactive.isHidden <~ viewA.reactive.values(forKeyPath: #keyPath(isHidden))
Note that the take(during:) is no longer necessary when using <~, as <~ automatically ties disposal of the binding source to the lifetime of the binding target.
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(_:))
I've seen objects which require the parameter, selector. What is the general concept in understanding a selector?
An example of choosing a selector is the NSTimer where my selector I have chosen is a function that increments the counter.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: ("incrementCounter"), userInfo: nil, repeats: true)
A selector is a concept from Objective-C that represents a message to be sent (i.e. a method to be called) dynamically at run time. When you set up something to be done via a selector, you know which message will be sent, but not necessarily what its parameters are. (And sometimes not even which object it'll be sent to.)
You can consider selectors a relative of closures/blocks, since they let you package up some code to be called later and hand it off to some other function. However, a closure must be specified/resolved at compile time, so it's less dynamic than a selector.
Selectors are great for "loose binding" concepts like control actions. You can use a selector to choose in Interface Builder which method a button should call when clicked, even though your app isn't actually running in IB; or you can say "this button should call paste: on whatever text view has keyboard focus", not knowing when you set up the button which view that'll be (because keyboard focus changes all the time).
Selectors in ObjC predate blocks/closures, so historically, selectors were the primary way to tell an API things like "call this method later", which is why you find them throughout Cocoa for patterns like timers, array sorting, and undo even when such patterns might benefit more from the tight binding of closures/blocks.
For more on using selectors in Swift, see Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C and/or this SO answer. For more on selectors and Cocoa in general, see Cocoa Core Competencies: Selector.
A selector is a custom method which is called in the class specified by target when the timer fires. If the method is supposed to take parameters each parameter is represented by a colon.
In Swift the struct Selector responds to the protocol StringLiteralConvertible,
Therefore – since the compiler knows the type in your example – a literal string is implicitly converted to a Selector instance and the Selector initializer is not needed.
PS: The parentheses around your selector string are not needed either.