Swift类型推断

Swift类型推断

hudson 译 原文

Swift 是一种静态类型语言,这意味着我们声明的每个属性、常量和变量的类型都需要在编译时指定。然而,通常情况下,这不是必须手动完成的事情,相反,编译器能够根据被分配的值自动推断出各种类型信息 --- 这得益于 Swift 支持类型推断的特性。

因此,例如,这里我们声明了几个常量 ------ 完全没有指定任何类型,因为编译器能够根据被分配的值推断出信息:

swift 复制代码
let number = 42
let string = "Hello, world!"
let array = [1, 1, 2, 3, 5, 8]
let dictionary = ["key": "value"]

为了对比,以下是如果我们手动指定每个常量的类型时的情况:

swift 复制代码
let number: Int = 42
let string: String = "Hello, world!"
let array: [Int] = [1, 1, 2, 3, 5, 8]
let dictionary: [String: String] = ["key": "value"]

因此,为使Swift 语法尽可能轻便,类型推断起着重要作用,类型推断不仅适用于变量声明和其他类型的赋值语句,而且在许多其他类型的情况下也是如此。

例如,这里我们定义了一个枚举,描述了各种联系人类型,以及一个函数,可以加载属于特定类型的Contact值数组:

swift 复制代码
enum ContactKind {
    case family
    case friend
    case coworker
    case acquaintance
}

func loadContacts(ofKind kind: ContactKind) -> [Contact] {
    ...
}

虽然通常会通过同时指定类型和成员(例如 ContactKind.friend)来引用上述枚举的成员,但由于类型推断,当在已知类型的上下文中引用枚举成员时,可以完全省略类型的名称 ------ 就像在调用上述函数时那样:

swift 复制代码
let friends = loadContacts(ofKind: .friend)

真正酷的是,上述的"点语法"不仅适用于枚举成员,在引用任何静态属性或方法时也是如此。例如,这里我们对Foundation 的URL 类进行扩展,增加了一个静态属性 ,用于创建一个指向这个网站的 URL

swift 复制代码
extension URL {
    static var swiftBySundell: URL {
        URL(string: "https://swiftbysundell.com")!
    }
}

现在,当调用任何接受URL 参数的方法时(例如新的 Combine框架增强的 URLSessionAPI),我们可以简单地引用上述属性:

swift 复制代码
let publisher = URLSession.shared.dataTaskPublisher(for: .swiftBySundell)

非常棒!然而,虽然类型推断是一种非常有用的特性,但仍然存在一些情况,可能需要额外指定一些类型信息以实现想要的结果。

这些情况中,一个非常常见例子是处理数值类型。当将数值文字分配给变量或常量时,它默认会被推断为 Int 类型 ------ 这是一个完全合理的默认值 ------ 但如果希望使用其他数值类型,比如 Double Float,就需要手动指定这些类型。以下是几种方法:

swift 复制代码
let int = 42
let double = 42 as Double
let float: Float = 42
let cgFloat = CGFloat(42)

还有一种情况是,在调用具有泛型返回类型的函数时,也可能需要给编译器提供额外的类型信息 。

例如,这里我们扩展了内置的 Bundle 类, 增加一个泛型方法,该方法能够轻松加载和解码应用程序中捆绑的任何 JSON 文件:

swift 复制代码
extension Bundle {
    struct MissingFileError: Error {
        var name: String
    }

    func decodeJSONFile<T: Decodable>(named name: String) throws -> T {
        guard let url = self.url(forResource: name, withExtension: "json") else {
            throw MissingFileError(name: name)
        }

        let data = try Data(contentsOf: url)
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    }
}

要了解更多关于 Swift 内置的错误处理机制(上面代码中,通过使用throwstry 关键字 )的信息,请查阅关于错误处理的基础文章

现在假设在应用程序开发中,在真实服务器和网络代码准备就绪之前,我们希望从捆绑的 JSON 文件中解码以下 User 类型的实例:

swift 复制代码
struct User: Codable {
    var name: String
    var email: String
    var lastLoginDate: Date
}

然而,如果像这样调用decodeJSONFile 方法,将会得到一个编译器错误:

swift 复制代码
// 错误:无法推断泛型参数 'T'
let user = try Bundle.main.decodeJSONFile(named: "user-mock")

这是因为即将解码的任何给定的 JSON 文件的确切类型取决于泛型类型 T 在每个调用点上实际引用的内容 ------ 而由于我们在上面没有给编译器提供任何此类信息,所以将得到一个错误。在这种情况下,编译器无法知道我们希望解码那种类型User 实例。

要解决这个问题,可以使用与上面用于指定不同类型数值的技术相同的技术,要么给我们的 user 常量一个明确的类型,要么使用 as 关键字 ------ 就像这样:

swift 复制代码
let user: User = try Bundle.main.decodeJSONFile(named: "user-mock")
let user = try Bundle.main.decodeJSONFile(named: "user-mock") as User

然而,如果在一个已知期望的返回类型的上下文中调用decodeJSONFile 方法,那么可以再次让 Swift 的类型推断机制找到该信息 ------ 就像在下面这种情况下那样,我们定义了一个名为 MockData 的包装器结构,该结构具有一个 User 类型的属性,我们将结果赋值给这个属性:

swift 复制代码
struct MockData {
    var user: User
}

let mockData = try MockData(
    user: Bundle.main.decodeJSONFile(named: "user-mock")
)

这就是对 Swift 类型推断能力的简要介绍。值得指出的是,类型推断确实有与之相关的计算成本,但幸运的是,这些成本完全发生在编译时(因此不会影响应用程序的运行时性能),但在处理更复杂的表达式时,仍然值得注意。如果遇到一个需要编译器很长时间才能弄清楚的表达式,那么我们总是可以使用上面的任何一种技术来手动指定这些类型。

谢谢阅读! 🚀

相关推荐
大熊猫侯佩15 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(五)
swiftui·swift·apple watch
大熊猫侯佩2 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩2 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩2 天前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩2 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
season_zhu2 天前
iOS开发:关于日志框架
ios·架构·swift
大熊猫侯佩3 天前
SwiftUI 中如何花样玩转 SF Symbols 符号动画和过渡特效
swiftui·swift·apple
大熊猫侯佩3 天前
SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
swiftui·swift·apple
大熊猫侯佩3 天前
使用令牌(Token)进一步优化 SwiftData 2.0 中历史记录追踪(History Trace)的使用
数据库·swift·apple
大熊猫侯佩3 天前
SwiftUI 在 iOS 18 中的 ForEach 点击手势逻辑发生改变的解决
swiftui·swift·apple