【问题标题】:Swift error: cannot convert value of type 'B -> (A) -> ()' to expected argument type '(A) -> Void'Swift 错误:无法将类型“B -> (A) -> ()”的值转换为预期的参数类型“(A) -> Void”
【发布时间】:2016-03-08 16:21:50
【问题描述】:

我有一个 Swift 程序会产生我无法理解的编译器错误。它归结为以下代码片段。

A 类是一个辅助类,在初始化时提供了一个稍后将调用的处理函数。这个函数有一个A 对象作为参数。

B 类初始化一个 A 类型的对象,并有一个函数 f 应该用作处理程序。或者,可以提供一个匿名代码块(闭包)。

class A {
    init(_: (A) -> Void) {}
}

class B {
    let a = A(f)
    func f(a: A) {}
}

这会产生以下编译器消息:

error: cannot convert value of type 'B -> (A) -> ()' to expected argument type '(A) -> Void'
let a = A(f)
          ^

Void() 是等价的,我想。

我什至不理解带有双 -> 的类型表达式。这是什么类型的?

谁能解释这里出了什么问题?还有,怎么做才对?

【问题讨论】:

    标签: swift syntax


    【解决方案1】:

    您不能在初始化实例属性(如a)时引用实例方法(如f),因为该实例正是我们正在创建的对象。一种解决方案是声明您的实例属性lazy,如下所示:

    class A {
        init(_: (A) -> Void) {}
    }
    
    class B {
        func f(a: A) {}
        lazy var a : A = A(self.f)
    }
    

    这是合法的,因为lazy 保证a 的初始化程序在稍后 创建实例 之前不会被评估。请注意,self 在这种情况下是绝对需要的(通常我建议您始终在可以使用的地方使用它)。

    另一种解决方案是将a 声明为A!(使其具有初始值,即nil)并稍后“真正地”初始化它。例如:

    class A {
        init(_: (A) -> Void) {}
    }
    
    class B {
        func f(a: A) {}
        var a : A!
        init() {
            self.a = A(self.f)
        }
    }
    

    【讨论】:

    • 感谢您为我指明了正确的方向。请查看我的回答,因为我有一些无法在评论中发表的评论:-)
    • 如果我的回答对你有帮助,请考虑接受。
    • 是的,当然。我仍然想知道类型表达式B -> (A) -> () 和错误消息的实际含义。 :-)
    • B -> (A) -> () 是您的func f 的类型。这是因为在 Swift 中,实例方法是柯里化的类方法。
    【解决方案2】:

    @matt 为我指明了正确的方向——非常感谢。他的解决方案是这里的a1 变体:

    class B {
        func f(a: A) {}
        lazy var a1 : A = A(self.f)       // ok
        lazy var a2 : A = A(f)            // error
        lazy var a3     = A(self.f)       // error
        lazy var a4     = A(f)            // error
    }
    

    有趣的是,所有其他变体a2a3a4工作。 a2a4 产生原始 “无法转换值” 错误,a3“使用未解析的标识符 'self'”

    lazy 解决方案的缺点是 a 现在是 var 而不是常量。我通过在初始化期间分配常量值找到了另一种方法:

    class B {
        let a: A
        init() {
            a = A() { (A) -> Void in … }  // ok
        }
    }
    

    有趣的是,这个 only 适用于闭包。代码

    class B {
        let a: A
        init() {
            a = A(f)                      // error
        }
        func f(a: A) { … }
    }
    

    给编译器错误“在所有存储的属性被初始化之前在方法调用'f'中使用'self'”

    尽管如此,即使在闭包变体中,self 也无法使用,因为它尚未完全初始化。

    【讨论】:

    • 对,但您现在回答的是不同的问题。您最初的问题假设一个我们希望引用的实例方法f。自然,如果没有这种方法,整个问题就会消失。 :))))
    • “有趣的是,这只适用于闭包。”不正确(如果通过“关闭”您的意思是“匿名功能”)。如果finit 内的本地 函数,它会工作得很好。正如我一直试图向您解释的那样,问题在于它何时是实例方法。
    • 是的,我相信我了解底层的“机制”。只要对象没有完全初始化,self 显然是不可访问的。即使这是在一个函数/闭包内完成的,当对象实际上在很久以前被初始化时(或者根本不会被调用),该函数/闭包将被调用。原因是位置管理类 (A) 使用完成处理程序调用 reverseGeocodeLocationB 类使用该位置管理器,并应在结果可用时立即通知。也许整个方法不是最优的,我应该改用委托。
    • 实际上,当您有异步代码并且需要稍后回调调用者时,我喜欢使用完成处理程序的想法。所以坚持这个,是我的建议。 ——但我提醒你,我的回答不是关于那个,因为那不是你问的。我的回答解释了您收到的编译器错误消息,即您的代码出了什么问题。
    • 我同意。委托总是意味着更紧密的耦合,因为提供服务的类必须知道使用服务的类,这通常是通过协议完成的。另一方面,委托策略意味着在初始化依赖方面的耦合不那么紧密,正如我们在这里看到的那样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-05
    • 2016-08-25
    • 2017-01-13
    • 2023-04-04
    • 1970-01-01
    • 2020-03-01
    相关资源
    最近更新 更多