【问题标题】:Is is possible to callAsFunction() variable that is Any?是否可以调用 Any 的 AsFunction() 变量?
【发布时间】:2020-02-16 19:40:22
【问题描述】:

最近我遇到了创建函数数组的需求。不幸的是,Swift 语言没有为函数提供顶级类型,而是必须通过它们的特定签名 (...)->(...) 来声明它们。所以我尝试编写一个可以容纳任何函数的包装器,然后将其专门用于仅容纳具有Void 返回类型和任意数量的参数的闭包。

struct AnyFunction {
    let function: Any
    init?(_ object: Any){
        switch String(describing: object){
            case let function where function == "(Function)":
                self.function = object
            default:
                return nil
        }
    }
    func callAsFunction(){
        self.function()
    }
}

随着我的进步,我发现这并非微不足道,可能需要一些内省的技巧,但尽管我尝试了,但我未能找到解决方案。编译器消息:

error: cannot call value of non-function type 'Any'

那么你将如何制作这个技巧,即明确地说,定义一个可以容纳任何函数类型的对象?

澄清: 我更喜欢定义如下内容:

typealias SelfSufficientClosure = (...)->Void
var a, b, c = 0
let funcs = FunctionSequence
    .init(with: {print("Hi!")}, {a in a + 3}, {b in b + 3}, { a,b in c = a + b})
for f in funcs { f() }

print([a, b, c])
//outputs 
//"Hi"
//3, 3, 6

PS 这个问题和这个问题有关 (Any or a trouble with sequence of functions)

【问题讨论】:

  • 什么是“任何功能类型”?您需要调用该函数,如果不同函数的签名不同,那么您的调用必须针对每个函数进行调整。我在你的代码中没有看到。那么你能澄清一下你想怎么称呼他们吗?
  • 当然。我想要的基本上是一组闭包[{print "Hi!"}, {someExternalSequence.apply{$0+1}}, {someObject.someValue = true}],以后可以在程序期间的某个时刻调用它们,并在需要时进行操作,这意味着添加或删除闭包。
  • 没有描述“任何函数类型”的类型(正如在回答您之前的问题时指出的那样)。如果有:如果您不知道它需要的参数的数量和类型,您将如何调用这样的元素? (Objective-C 使用 NSInvocation 类解决了这个问题。您可能可以在 Swift 中使用它,但只能将消息发送到 NSObject 子类的实例。)
  • @MartinR 这个 AnyFunction 包装器用于证明不同的函数可以(或不能看起来)按顺序排列的概念。后来它将成为一个(...)->Void 特定类型,它拥有自给自足的闭包,这意味着那些已经捕获了一些上下文的闭包。 var a, b, c = 0 let funcs = FunctionSequence(with: {print("Hi!")}, {a in a + 3}, {b in b + 3}, { a,b in c = a + b}) print([a, b, c], separator: " ")
  • 我当然可能错了,但我相当肯定 Swift 中没有“任何函数”类型。另见docs.swift.org/swift-book/LanguageGuide/Functions.html“Swift 中的每个函数都有一个类型,由函数的参数类型和返回类型组成。”

标签: swift function callable any


【解决方案1】:

函数是从输入到输出的映射。在您的示例中,您的输入是无效的(没有输入),您的输出也是无效的(没有输出)。所以这种函数就是() -> Void

我们可以根据您的称呼来判断这是正确的类型:

for f in funcs { f() }

您希望f 是一个不接受输入也不返回输出的函数,这正是() -> Void 的定义。通过使用该类型(并清除一些语法错误),我们可以准确地获得您期望的输入和输出:

var a = 0, b = 0, c = 0

let funcs: [() -> Void] = [{print("Hi!")}, {a = a + 3}, { b = b + 3}, { c = a + b}]
for f in funcs { f() }

print(a, b, c, separator: ",")
//outputs
//Hi!
//3, 3, 6

当您编写像 {a in a + 3} 这样的闭包时,这并不意味着“捕获 a”(我相信您会期待这一点)。这意味着“这是一个闭包,它接受一个名为a 的参数(与同名的全局变量完全无关)并返回该值加3。”如果这就是你的意思,那么当你调用f() 时,你需要传递一些东西并用返回值做一些事情。

【讨论】:

  • 感谢您的澄清。我承认我打滑了。(
  • 另外,你知道编译器是否会为了一些优化而改变执行顺序吗?如果是这样,如何禁止它这样做?
  • 不,不会改变执行顺序。
【解决方案2】:

您可以使用枚举将各种函数放入数组中,然后通过开关提取函数。

 enum MyFuncs {
        case Arity0 ( Void -> Void )
        case Arity2 ( (Int, String) -> Void)
    }

    func someFunc(n:Int, S:String) { }
    func boringFunc() {}
    var funcs = Array<MyFuncs>()
    funcs.append(MyFuncs.Arity0(boringFunc))
    funcs.append( MyFuncs.Arity2(someFunc))

    for f in funcs {
        switch f {
        case let .Arity0(f):
            f()  // call the function with no arguments
        case let .Arity2(f):
            f(2,"fred") // call the function with two args
        }
    }

【讨论】:

  • 这将是一个巨大的枚举,您如何跟踪参数值?
  • @JoakimDanielson 他们可以修改我的枚举,意味着使用 Any 而不是 Int 或 String。
  • @SGDev 仍然需要枚举所有可能的组合,以使该解决方案适用于非常具体的情况。此外,如果必须将其作为框架,那么遗憾的是,您的解决方案不允许这样做
  • @oneWayTicket 是的,您需要指定所有用例。
猜你喜欢
  • 1970-01-01
  • 2016-03-30
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多