Folks, I’m back
I write it once a year, one batch at a time, one batch after a year off, and then I come back before WWDC 20, because I have new material. Swift has been maturing since 5.1, so most of the new language features in Swift 5.2 are minor tweaks. Now that they are available in the latest Xcode release, let’s take a look. This topic: callAsFunction
Imagine that
In my freshman C++ class, my teacher introduced the function call operator. Wow, it’s amazing that an object can be called as a function. It also has a nice, right? The names of function objects and functors are also called. Let’s start with a section of C++.
struct Adder
{
public:
Adder(int base):m_base(base){}
int operator()(int a) {
returnm_base + a; } private: int m_base; }; int main(int argc, const char * argv[]) { int adder = Adder(2); cout << adder(6) << endl; / / 8return 0;
}
Copy the code
A function is a function. Why make it so complicated? The reason is that unlike normal functions, Adder is stateful. Lambda was later introduced in C++ 11 (similar to Swift’s closure), and a common implementation of lambda is to compile into the function object above.
Characteristics of the last century
Like function objects in C++ 98, the callAsFunction in Swift 5.2 is a thoroughly last century feature. Kotlin also has a similar Invoke Operator, so it’s not an invention for Swift, just a determination to add this little syntactic sugar. Let’s take a look at Swift’s version:
struct Adder {
var base: Int
func callAsFunction(_ x: Int) -> Int {
return base + x
}
}
let add2 = Adder(base: 2)
print(add2(6)) // 8
Copy the code
The callAsFunction here (…). Function, the name of the Swift compiler convention, which can take 0-N arguments, even indefinite ones. Except for the convention of function names, there’s basically nothing that limits what you can do, you can use generics, you can even use class or enum, and of course these objects are usually value-type semantics, so struct is the most common type, enum is also the most common, To define a class, remember the overhead of the reference type and add an init constructor to it.
Students may ask why the C++ example above can’t be class. The main difference between a class and a struct in C++ is what the default access level is for a member, and in the above example we specified the access level. In C++, object construction is user-controlled on the heap or stack, which is different from Swift.
The following example shows that callAsFunction is just a syntactic sugar that can be called directly with that name.
let add2 = Adder(base: 2)
print(add2(6)) // 8
print(add2.callAsFunction(6)) // 8
Copy the code
Swift Callables
In Swift this is not a function, but you can call objects like functions what else? Let’s take stock:
- The value of the function type, in Swift the function is a first-class citizen, has a type, has a value. so
callAsFunction
It can also be assigned to a function type to be called later. This is not forcallAsFunction
The same is true for methods with other names.
let f: (Int) -> Int = add2.callAsFunction(_:)
print(f(6)) // 8
Copy the code
-
Type names, we often use UIView(…) To construct an object, but actually this syntax sugar is converted to a function call uiView.init (…).
-
The @dynamicCallable modifier used to interact with dynamic languages.
@dynamicCallable struct DummyCallable {
func dynamicallyCall(withArguments: [Int]) {
print(withArguments)
}
func dynamicallyCall(withKeywordArguments: KeyValuePairs<String, Int>) {
print(withKeywordArguments)
}
}
letX = DummyCallable () x (1, 2, 3) / / [1, 2, 3] x (Leon: 28, 178) / / /"Leon": 28."": 178]
Copy the code
To some extent, the callAsFunction can be regarded as a static companion of this form.
Scan the QR code below to follow “Interviewer Xiaojian”