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:

  1. The value of the function type, in Swift the function is a first-class citizen, has a type, has a value. socallAsFunctionIt can also be assigned to a function type to be called later. This is not forcallAsFunctionThe same is true for methods with other names.
let f: (Int) -> Int = add2.callAsFunction(_:)
print(f(6)) // 8
Copy the code
  1. Type names, we often use UIView(…) To construct an object, but actually this syntax sugar is converted to a function call uiView.init (…).

  2. 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”