edopelawi edopelawi - 3 months ago 10
Swift Question

Different result from #function and #selector in Swift

I tried to detect whether a method has been called using

#function
in Swift, but it returns different result than the
#selector
's description. Here's a working code:

class SampleClass {

var latestMethodCall: Selector?

@objc func firstMethod() {
latestMethodCall = #function
}

@objc func secondMethod(someParameters: Int, anotherParameter: Int) {
latestMethodCall = #function
}

func isEqualToLatestMethod(anotherMethod anotherMethod: Selector) -> Bool {
return latestMethodCall?.description == anotherMethod.description
}
}


let sampleObject = SampleClass()

sampleObject.firstMethod()

let expectedFirstMethod = #selector(SampleClass.firstMethod)

if sampleObject.isEqualToLatestMethod(anotherMethod: expectedFirstMethod) {

print("Working correctly!")

} else {

print("Broken selector...")

if let latestMethodCall = sampleObject.latestMethodCall {
print("Object's latest method call: \(latestMethodCall.description)") // prints firstMethod()
print("Expected method call: \(expectedFirstMethod.description)") // prints firstMethod
}
}

sampleObject.secondMethod(5, anotherParameter: 7)

let expectedSecondMethod = #selector(SampleClass.secondMethod(_:anotherParameter:))

if sampleObject.isEqualToLatestMethod(anotherMethod: expectedSecondMethod) {

print("Working correctly!")

} else {

print("Broken selector...")

if let latestMethodCall = sampleObject.latestMethodCall {
print("Object's latest method call: \(latestMethodCall.description)") // prints secondMethod(_:anotherParameter:)
print("Expected method call: \(expectedSecondMethod.description)") // prints secondMethod:anotherParameter:
}
}


From the sample above, I found that
#function
returns Swift-y description, while
#selector
returns ObjC-y description. Is this expected? Or did I done something wrong?

Thank you for taking your time! :)

Answer

In Swift < 3.0 Selector type can be initialized by String. Since doing this is unsafe (this approach came from dynamic Objective-C), #selector introduced in Swift 2.2. #selector will not allow you to define Selector for method that not exist.

Now, lets look deeper to initializing Selector by string. String for selector should have strictly special signature: for method without arguments func firstMethod() it is just method name without braces: "firstMethod". On other hand, #function keyword made absolutely not for defining Selector's, and it returns function label with braces:

@objc func firstMethod() {
    print(#function)
}
//firstMethod()

If you will define method with arguments, you will see that #function now will return same but without braces

@objc func firstMethod(param: Int) {
    print(#function)
}
//firstMethod

since Selector, again, expect other signature for it: "firstMethod:".

Conclusion: for defining Selector use #selector; in Swift >= 3.0 you will have no choice anyway.

Comments