Swift 5.2 将实例作为函数调用

作者: 韦弦Zhy | 来源:发表于2020-03-10 00:17 被阅读0次

Swift 5.2中的一个新功能是可以将类型实例作为函数调用(callAsFunction)。或者,如Swift Evolution 提案所述,“用户定义的标称类型的可调用值”。此函数的简短描述是,它允许您调用实现了callAsFunction方法的任何类型的实例,就好像它是一个函数一样

callAsFunction
例如一个计算每年财富值以8%递增的计算器,传递一个初始值以及经过多少年,得出最终的财富:
struct InvestmentsCalculator {
  let input: Double
  let averageGrowthPerYear = 0.08

  func callAsFunction(years: Int) -> Double {
    return (0..<years).reduce(input, { value, _ in
      return value * (1 + averageGrowthPerYear)
    })
  }
}

let calculator = InvestmentsCalculator(input: 1000)
let newValue = calculator(years: 10)

实现了callAsFunction方法后,可以直接将实例当做函数使用,他的具体实现就是callAsFunction的实现。

尽管这很酷,但您可能更想知道这样的功能在实际编程过程中何时有用,以及如何在代码中应用它。

Swift不是唯一允许其用户调用某些类型的实例作为函数的语言,比如:

它是 @dynamicCallable 的静态形式

SE-0216引入了用户定义的动态可调用值。在考虑的可替代方案部分中,要求我们与提议的动态版本一起设计和实现该提议的“静态可调用”版本。有关“静态可调用项”的讨论,请参照pitch thread

在Swift中使用callAsFunction相对简单。任何定义callAsFunction方法的对象都可以视为一个函数。您的callAsFunction可以接受参数并返回值,如Swift Evolution建议中所示,并带有以下示例:

struct Adder {
  let base: Int

  func callAsFunction(_ x: Int) -> Int {
    return base + x
  }
}

let add3 = Adder(base: 3)
add3(10) // 13

您甚至可以在一个对象上有多个重载:

struct Adder {
    var base: Int

    func callAsFunction(_ x: Int) -> Int {
        return base + x
    }

    func callAsFunction(_ x: Float) -> Float {
        return Float(base) + x
    }

    func callAsFunction<T>(_ x: T, bang: Bool) throws -> T where T: BinaryInteger {
        if bang {
            return T(Int(exactly: x)! + base)
        } else {
            return T(Int(truncatingIfNeeded: x) + base)
        }
    }
}

let add1 = Adder(base: 1)
add1(2) // => 3
try add1(4, bang: true) // => 5

当类型检查失败时,错误消息看起来类似于函数调用的错误消息。如有歧义,编译器将显示相关的callAsFunction方法候选对象。

add1("foo")
// error: cannot invoke 'add1' with an argument list of type '(String)'
// note: overloads for functions named 'callAsFunction' exist with these partially matching parameter lists: (Float), (Int)
add1(1, 2, 3)
// error: cannot invoke 'add1' with an argument list of type '(Int, Int, Int)'

直接引用callAsFunction

由于callAsFunction方法是一种普通方法,因此可以使用其声明名称引用callAsFunction方法,并获取捕获self的闭包。这正是今天方法引用的工作方式。

let add1 = Adder(base: 1)
let f1: (Int) -> Int = add1.callAsFunction
let f2: (Float) -> Float = add1.callAsFunction(_:)
let f3: (Int, Bool) throws -> Int = add1.callAsFunction(_:bang:)

当类型也是@dynamicCallable时

类型既可以具有callAsFunction方法,也可以使用@dynamicCallable声明。在对调用表达式进行类型检查时,类型检查器将首先尝试将调用解析为函数或初始化程序调用,然后将其解析为callAsFunction方法调用,最后是动态调用。

能够决定是否要让callAsFunction实现接受参数以及返回类型是什么的能力使其成为一个非常强大的功能。您确实可以根据自己的需求自定义此功能,并且由于可以向对象添加多个callAsFunction重载,因此可以在多个上下文中将单个对象用作函数。

相关文章

  • Swift 5.2 将实例作为函数调用

    Swift 5.2中的一个新功能是可以将类型实例作为函数调用(callAsFunction)。或者,如Swift ...

  • swift 5.2更新内容

    Swift 5.2 Released! SE-0249:将Key Path表达式作为函数:(Root)->Valu...

  • 第十四节 Swift中的方法

    Swift 中的方法就是函数,分为实例方法和类型方法。 实例方法在特定类型实例中调用,代码示例 Couter 类定...

  • Swift类与OC类方法相互调用的

    1、OC调用Swift实例方法 例如在ViewController.m类里调用Swift的logMe实例方法,就可...

  • Swift 5.2 将KeyPaths用作函数

    Swift 5.2的新功能之一:能够将KeyPaths用作函数。如果您只在闭包中返回某个KeyPath的值,这将非...

  • 函数

    简单函数格式 定义 调用函数 传参函数格式: 传参数 定义 注意 重点 实例(简单) 实例(传参) 实例函数(调用...

  • Swift函数调用逆向分析

    简介: ##逆向分析Swift代码 为了研究Swift的动态性,我们将Swift的类、成员变量、函数调用等代码进行...

  • 面向对象继承的方式

    创建父类 原型链继承:将父类的实例作为子类的原型 借用构造函数继承:在子类型构造函数的内部调用父类的构造函数 组合...

  • Swift 调用 Objective-C 的可变参数函数

    Swift 调用 Objective-C 的可变参数函数 Swift 调用 Objective-C 的可变参数函数

  • js函数对象和回调

    函数对象和实例对象 函数对象:将函数作为对象使用时,简称函数对象实例对象:new函数产生的对象,简单对象 ()的左...

网友评论

    本文标题:Swift 5.2 将实例作为函数调用

    本文链接:https://www.haomeiwen.com/subject/yegwrhtx.html