Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?
I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:
addObserver(self, function: someCallback, name: "some notification", object: nil)
But addObserver takes a String selector argument.
You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):
addObserverForName:object:queue:usingBlock:
So just use that.
I'd still like to find an answer to my original question, but based on #matt's suggestion, I'm currently using this extension:
extension NSNotificationCenter {
class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
}
}
Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:
NSNotificationCenter.addObserver(doSomething, name: "some notification")
I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.
Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.
I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).
Instead, I would play this game:
Create an dictionary property mapping [String: Void -> ()] (string to function)
When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:
(assuming this is good Swift; I haven't tried it):
void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
let name = // strip the leading stuff off of _cmd
if let f = self.dispatchTable[name] {
f()
}
}
(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)
Related
Back in objective-C with ARC, this wasn't safe to do:
MyClass* someObject = ...
__weak MyClass* weakSomeObject = someObject;
doSomething(^{
[weakSomeObject someMethod];
});
Why? because simply calling a method doesn't cause ARC to retain the object, and thus the someObject instance might be released and dealloced in the middle of the execution of someMethod
Bringing this forward into swift, it translates as follows:
let someObject: MyClass = ...
doSomething { [weak someObject]
someObject?.someMethod()
}
My question is, what are the semantics of the ?. operator in swift regarding ARC, and is it safe to use with weak reference method calls?
I can imagine the swift compiler translating the above code into something like this:
let someObject: MyClass = ...
doSomething { [weak someObject]
if let tmp = someObject {
tmp.someMethod()
}
}
If it did that, then it would indeed be safe, as tmp is a strong reference and thus would cause ARC to retain the object across the duration of the call to someMethod
However, I could also imagine it translating into something without an ARC retain for performance reasons or whatever.
Does anyone know what it actually does, and is there a specification or document that makes this explicit?
Bullet points to get first:
closures capture context.
if let, guard etc only checks if optional contains actual value but doesn't capture it's context.
Now about ?. It's optional chaining, if object on which method is called is nil, it won't call method as well as fail full chain(if you follow up return value from method with another method call or else).
About weak and unowned references to used objects inside closure:
weak doesn't create strong reference to object, so it makes it optional because object may not be there when it gets called
unowned as well as weak doesn't create strong reference, however object won't be optional, so you must guarantee that closure won't outlive object it uses, because you will get crash referencing object that isn't there.
Hope it helps.
Short answer is yes, it is safe. Longer explanation and link follow:
Methods defined for a type in Swift are curried functions like this:
class MyClass {
func doSomething() {
// I can access self in here
}
}
print(type(of: MyClass.doSomething)) //(MyClass) -> () -> ()
In other words, the function defined on the type is curried such that it must first be passed a reference to what will be self, which is strongly captured in the returned partially applied function (()->())
When you call the method on an instance of MyClass (not the type itself), it automatically does the initial partial application of passing in that instance of self which is then captured strongly by the resulting closure that is actually invoked.
So the type of MyClass.doSomething is (MyClass) -> () -> (), and the type of MyClass().doSomething is () -> (), where the initial MyClass reference is already captured inside the () -> ().
Given the below code:
weak var test: MyClass? = MyClass()
test?.doSomething()
On line 2, a couple things happen. First, if test is non-nil, it will be passed to the the method implementation on the type MyClass and captured by the MyClass type's curried function (MyClass) -> () ->(). This captured reference is the "self" that can be used inside that instance method.
Then, the resulting partially applied function of type ()->() is invoked, and the strong reference / capture of self from above is released once the function executes.
So, the answer is then yes, it is safe to call instance methods on a weak reference, because the instance method actually strongly captures the instance up until and while it is executing.
This post discusses how instance methods in Swift are curried in more detail, and links to the initial dev forum source of the information.
https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/
UPDATE:
Some additional interesting and in-depth articles on this mechanism:
http://rosslebeau.com/2016/sneaky-reference-cycles-swift-instance-methods
https://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/
I am learning swift using learnxinyminutes.com
I'm having a hard time understanding a component of on of the examples on the site (below), namely the use of underscore in the let statements instead of variable names.
// Variadic Args
func setup(numbers: Int...) {
let _ = numbers[0]
let _ = numbers.count
}
I understand that swift wants you to use underscore if you never declare the variable, but 1) why would you want to declare variables you never use? And 2) is there a way to get those values out if you used _. And, 3) if so, how?
1) why would you want to declare variables you never use?
Generally you don't! There are some cases though where you might want to, such as the example Danny Buonocore gave. Another example might be this: say you want to check that a variable is non-nil before you do something, though you do not need that variable for the subsequent code. (A guard statement is probably more useful in these situations).
if let _ = titleString {
// Do something that doesn't use titleString, but where it being non-nil means this operation is valid
}
Another example is with Swift functions. Names for a second, third, etc. parameter must be identified whenever calling a function. For example:
func externalizedParameters(first: Int?, second: Int?) {
...
}
Which is called using externalizedParameters(5, second: 6).
But if you are doing something trivial, and the function name already makes it clear what the two parameters are, such as with a swap operation, you might not want to force the caller to explicitly state the name of the second parameter. In that case, you can use '_' because you don't care about the name of that externalized parameter:
func swap(first: Int?, _ second: Int?) {
...
}
This can then be called as swap(5, 6) as opposed to swap(5, second: 6).
2) is there a way to get those values out if you used _. & 3) if so, how?
No. You need to name the variable if you want to use the value.
One example would be a loop where you don't necessarily need the index.
for _ in 0..<10 {
}
In swift you can also externalize the names of the parameters. This allows the caller to pass by name, rather than order:
func myFunction(param1:String, param2:String) {
}
myFunction(param2: "second value", param1: "first value");
If you don't want to externalize the name of a parameter, you can include an underscore before it, like so:
func myFunction(param1:String, _ param2:String) {
}
In this case, you must pass the value to set param2 to as the second argument, and you cannot use the naming scheme seen in the first example.
If you need to use the value inside of a variable, declare it with a name and not _. The underscore says, I know this call returns a value but we're not going to use it, so it doesn't need a name. Swift emits warnings for unused function call results, so this is a way to suppress that warning.
I am switching over the syntax of my project toward Swift 2.2 (which xCode helps me do automatically); however, I do not understand the new #selector() syntax.
As an example:
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self,
selector: #selector(MyVC.timerCalled(_:)), //new selector syntax!
userInfo: nil, repeats: true)
This has the selector #selector(MyVC.timerCalled(_:))
What does the _: signify? Can you add other variables into this selector? Say, #MyVC.timerCalled(_:whateverVar).
General info on what is different in this syntax as opposed to the string based implementation from earlier versions of Swift are greatly appreciated.
The bit in parenthesis is a mechanism for identifying the argument list for the selector that you want.
I recommend you look at the Generalized Naming proposal from Swift Evolution. It covers cases where you have a number of functions that differ only by their parameter labels and need to refer to them. The example from that document is:
extension UIView {
func insertSubview(view: UIView, at index: Int)
func insertSubview(view: UIView, aboveSubview siblingSubview: UIView)
func insertSubview(view: UIView, belowSubview siblingSubview: UIView)
}
If you wanted to get a function value for one of those the result is ambiguous:
let fn = someView.insertSubview // ambiguous: could be any of the three methods
The solution implemented is to add the argument labels, without any type information to the code that generates the function value to disambiguate which you want:
let fn = someView.insertSubview(_:at:)
let fn1 = someView.insertSubview(_:aboveSubview:)
See how the labels are added in the parens?
This proposal played a role in the one that most directly applies to your question:
Referencing the Objective-C selector of a method
In this particular case the selector you want to refer to is timerCalled: which is a function of one parameter that has no label. Hence (_:). The underscore means the label is not specified and the colon.
Swift 2.2 has deprecated Stringified selectors: In swift 2.0, we use to write the selector as a String i.e "buttonClicked". The disadvantage with this approach is that the compiler can't check whether the method really exists or not at compile time(Even if you have misspelled it).
EX:1
func buttonClicked(){
}
So the above method in the new approach can be called as #selector(buttonClicked)
EX:2
func buttonClicked(stringValue : String){
}
So the above method in the new approach can be called as #selector(buttonClicked(_:))
EX:3
func buttonClicked(stringValue : String, indexValue : Int){
}
So the above method with parameters in the new approach can be called as #selector(buttonClicked(_:indexValue:))
Consider below code for adding target to button in swift 3 using #selector
button.addTarget(self, action: #selector(self.buttonAction(sender:)),
for: UIControlEvents.touchUpInside)
func buttonAction(sender:UIButton!){
}
This syntax worked for me when migrating to swift 3
This is the way Swift method signatures are represented in documentation, and it is now beginning to be used in new language features such as the #selector() syntax to express methods by their argument lists.
The each colon (:) represents a method parameter. For named parameters, the colon is preceded by the external parameter name; for unnamed parameters, an underscore (_) is used.
So for example, MyVC.timerCalled(_:)) indicates a method on the MyVC type with one unnamed parameter, which might be declared like this:
func timerCalled(timer: NSTimer) { ... }
(Note that timer is the internal parameter name, since by default the first parameter of a method is unnamed)
The type (MyVC in your example) can also be omitted if it is within the same scope of the #selector() declaration.
A more complex example might look like this:
let sel = #selector(aMethodWithSeveralParameters(_:secondParam:thirdParam:))
...
func aMethodWithSeveralParameters(firstParam: Int, secondParam: Int, thirdParam: Int) { ... }
I am trying to understand how parameters passed to a method are available to nested closures. I'm nervous that something I wrote won't always have the original parameters available.
(these are drastically simplified examples)
I have a method that I wrote that specifies a closure as a parameter:
func saveNameAndAgeToServer(serverParams: [String:String], completionHandler: (age: NSNumber) -> ()) {
// Connect to a server
// POST a name and dob from serverParams
// Receives a current age in completion:
completionHandler(age: 22)
}
Now somewhere else I create another method, that also specifies a closure, takes two parameters and calls the first function:
func AwesomeFunc(name: String, dob: NSDate, completionHandler: (isOverTwentyOne: Bool) -> ()) {
let formattedDob = NSDateFormatter().stringFromDate(dob)
saveNameAndAgeToServer([name:formattedDob]) { (age) -> () in
if (age as Int) >= 21 {
print("\(name) can have a beer.")
completionHandler(isOverTwentyOne: true)
} else {
print("\(name) is too young to drink, he can have a water.")
completionHandler(isOverTwentyOne: false)
}
}
}
Am I able to guarantee that the parameters (name and dob) passed into this latter function will always be available?
What I'm trying to ask is will the memory that the saveNameAndAgeToServer closure runs within always have the parameters of AwesomeFunc available to it? I'm pretty sure the function is all being held while whatever it calls is completed but would love a 2nd opinion.
You are correct, captured variables will last for the life of the closure. Here's an exert on capturing variables from apple's swift documentation:
A closure can capture constants and variables from the surrounding
context in which it is defined. The closure can then refer to and
modify the values of those constants and variables from within its
body, even if the original scope that defined the constants and
variables no longer exists.
In Swift, the simplest form of a closure that can capture values is a
nested function, written within the body of another function. A nested
function can capture any of its outer function’s arguments and can
also capture any constants and variables defined within the outer
function.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
I'm working, tentatively, with the AudioToolbox API using Swift 2.0 and Xcode 7b6. The API uses a lot of c-language constructs, including function pointers. This is my first time working with commands like withUnsafeMutablePointer and unsafeBitCast. I am looking for a reality check to make sure that I am not way off base in what I am doing.
For example, to open a file stream, you use the following function:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
Just the type signature of the function makes me start to sweat.
At any rate, the inClientData parameter needs to be an UnsafeMutablePointer<Void>, and the pointer will point to an instance of the same class I am working in. In other words, it needs to be a pointer to self. My approach is to call the function using withUnsafeMutablePointer like this:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen($0, AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
My first question is whether or not I'm using withUnsafeMutablePointer correctly here. I wasn't sure how to get a pointer to self - just writing &self doesn't work, because self is immutable. So I declared proxy as a variable and passed a reference to that, instead. I don't know if this will work or not, but it was the best idea I came up with.
Next, AudioFileStreamPropertyListener and AudioFileStreamPacketsListener are C callback functions. They each get passed the pointer to self that I created using withUnsafeMutablePointer in AudioFileStreamOpen. The pointer is passed in as an UnsafeMutablePointer<Void>, and I need to cast it back to the type of my class (AudioFileStream). To do that, I believe I need to use unsafeBitCast. For example, here is AudioFileStreamPropertyListener:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
That compiles fine, but again I'm not sure if I'm using unsafeBitCast correctly, or if that is even the correct function to be using in this kind of situation. So, is unsafeBitCast the correct way to take an UnsafeMutablePointer<Void> and cast it to a type that you can actually use inside of a C function pointer?
It's interesting that the inClientData "context" param is bridged as UnsafeMutablePointer, since I doubt the AudioToolbox APIs will modify your data. It seems it would be more appropriate if they'd used COpaquePointer. Might want to file a bug.
I think your use of withUnsafeMutablePointer is wrong. The pointer ($0) will be the address of the variable proxy, not the address of your instance. (You could say $0.memory = [a new instance] to change it out for a different instance, for example. This is a bit confusing because its type is UnsafeMutablePointer<MyClass> — and in Swift, the class type is itself a pointer/reference type.)
I was going to recommend you use Unmanaged / COpaquePointer, but I tested it, and realized this does exactly the same thing as unsafeAddressOf(self)!
These are equivalent:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
And these are equivalent:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
While the Unmanaged approach makes logical sense, I think you can see why it might be prefereable to use unsafeAddressOf/unsafeBitCast :-)
Or, you might consider an extension on Unmanaged for your own convenience:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
Then you can use:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
Of course, you will need to ensure that your object is being retained for the duration that you expect it to be valid in callbacks. You could use passRetained, but I would recommend having your top-level controller hold onto it.
See some related discussion at https://forums.developer.apple.com/thread/5134#15725.