【问题标题】:Swift 4: "Non-nominal type 'T' does not support explicit initialization"Swift 4:“非标称类型 'T' 不支持显式初始化”
【发布时间】:2017-11-17 10:35:16
【问题描述】:

我正在使用这个扩展来检索 urlSession 回调中的 HTTP 标头:

extension HTTPURLResponse {

    func get(_ header: String) -> String? {
        let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }

        if let headerValue = keyValues.filter({ $0.0 == header.lowercased() }).first {
            return headerValue.1
        }
        return nil
    }

}

这很好用,让我可以像这样检索标题元素:

    if let metaInt = httpResponse.get("icy-metaint") {
        self.icyMetaInt = Int(metaInt)
    }

现在我希望通过引入一个泛型函数使它更优雅一点:

func getHeader<T>(_ httpResponse: HTTPURLResponse, _ headerName: String) -> T? {
        guard let element = httpResponse.get(headerName) else {
            return nil
        }
        return T(element)
}

目的是使用这样的语句:

self.icyMetaInt = getHeader(httpResponse, "icy-metaint")

我相信类似的东西可以在 C# 中使用。但是 Swift 编译器抱怨:

非标称类型 'T' 不支持显式初始化

return T(element)

即使我使用

return T.init(element) 

有没有机会实现我的想法?

编辑:添加了 T? (一定是在编辑时丢失了)

【问题讨论】:

  • 泛型不是这样工作的。即使他们这样做了,你也没有告诉编译器 T 是什么。
  • 另外,您正试图从一个不返回可选项的函数返回 nil
  • 是的,你是对的。需要加T吗?但是,这是泛型函数在 C# 中运行良好的方式。问题是,编译器知道 T,因为赋值 self.icyMetaInt(它是 Int?)说明了一切。我看不出有什么理由,为什么那不应该起作用。但如果它在 Swift 中不起作用,那么它就不起作用。没问题
  • 问题是:Swift 允许我像这样创建两个函数: func getHeader(_ httpResponse: HTTPURLResponse, _ headerName: String) -> String? { guard let element = httpResponse.get(headerName) else { return nil } return String(element) } func getHeader(_ httpResponse: HTTPURLResponse, _ headerName: String) -> Int? { guard let element = httpResponse.get(headerName) else { return nil } return Int(element) } “通用”解决方案会更好......
  • 泛型不是由返回上下文决定的。它由对象实例确定,因此您需要一个 HTTPURLResponse&lt;Int&gt; 的实例,以便编译器知道 T 是一个 Int,然后您不能将另一个标头值解释为字符串,而且它也因为您无法控制响应对象的实例化,所以无法正常工作。

标签: ios swift generics


【解决方案1】:

发生这种情况是因为编译器不知道它应该调用哪个初始化程序,因为您正在做的事情太笼统了,正如错误所述。 你想要达到的目标是可以做到的,但实施取决于你要达到的目标。

简而言之,如果您希望能够初始化许多不同的类型,无论是原语还是对象,您都应该定义一个新协议,扩展每种类型以根据您认为合适的方式实现此协议,并限制 T 类型到这个协议。

示例:

protocol HTTPHeaderProtocol {
    init(_ headerProtocolString: String?)
}

class MyCustomObject: HTTPHeaderProtocol {
    var myString = ""
    required init(_ headerProtocolString: String?) {
        self.myString = headerProtocolString ?? ""
    }
    
}

extension String: HTTPHeaderProtocol {
    init(_ headerProtocolString: String?) {
        self.init(headerProtocolString)
    }
}

extension Int: HTTPHeaderProtocol {
    init(_ headerProtocolString: String?) {
        self.init(headerProtocolString)
    }
}

extension HTTPURLResponse {
    
    func get(_ header: String) -> String? {
        let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }
        
        if let headerValue = keyValues.filter({ $0.0 == header.lowercased() }).first {
            return headerValue.1
        }
        return nil
    }
    func getHeader<T:HTTPHeaderProtocol>(_ httpResponse: HTTPURLResponse, _ headerName: String) -> T? {
        guard let element = httpResponse.get(headerName) else {
            return nil
        }
        return T(element)
    }
}

这会起作用,希望有所帮助:)

【讨论】:

  • 谢谢。但这看起来(没有冒犯)真的很难看 :) 我通过将变体添加到 HTTPURLResponse 扩展来解决问题。恕我直言,应该可以像我打算的那样做泛型,因为在这种情况下,它允许编写“真正的”泛型函数。编译器应该能够检测在什么上下文中调用函数以解析正确的 T。没关系。我有我的解决方案。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多