Swift 类型系统入门:从 Int、String 到自定义类型

专栏 :Swift语言精进之路
编号 :A02 · 系列第 2 篇
字数 :约 4500 字
标签:Swift / 类型系统 / 类型推导 / 元组 / 嵌套类型 / 类型别名


前言

Swift 被很多人称为「类型安全的语言」,但类型安全到底意味着什么?

简单说:编译器在编译阶段就能发现大量错误,而不是等到用户用到那一行代码时才发现。

类型系统是 Swift 最核心的设计决策之一。今天我们从最基础的数据类型开始,理解 Swift 的类型系统是如何工作的,以及它如何帮助我们写出更安全的代码。


一、Swift 类型的核心原则

1.1 类型安全 = 编译时检查

swift 复制代码
// ❌ 这行代码在 Swift 中无法编译
let age: Int = "twenty-five"  // Error: Cannot convert String to Int

// ✅ Swift 的类型是强制的,不能隐式转换
let age: Int = 25
let name: String = "Alice"

Swift 不允许隐式类型转换------这和 JavaScript、Python 完全不同:

swift 复制代码
// JavaScript(隐式转换)
let result = "5" + 3  // "53" (字符串拼接,行为可能不符合预期)

// Swift(必须显式转换)
let result = Int("5")! + 3  // 8 (明确知道在做什么)

1.2 类型推导:让代码简洁但不牺牲安全

Swift 可以从上下文推导出变量类型,但一旦确定就不能改变

swift 复制代码
// Swift 自动推导出 name 是 String
let name = "Alice"
name = "Bob"      // ✅ 同一个类型,OK
name = 123        // ❌ Error: Cannot assign Int to String

1.3 基本类型一览

类型 说明 示例
Int / UInt 有符号/无符号整数 42, -10
Double / Float 浮点数 3.14, 1.0e-5
Bool 布尔值 true, false
String 字符串 "Hello"
Character 单个字符 "A"
Array<T> 数组 [1, 2, 3]
Dictionary<K,V> 字典 ["key": "value"]
Set<T> 无序集合 Set([1, 2, 3])
Optional<T> 可选值 Int?

二、整数的精确选择

2.1 Int 的平台相关大小

swift 复制代码
// Int 的大小根据平台自动选择
// 64位 macOS/iOS: Int = Int64
// 32位平台: Int = Int32

print(Int.min)  // -9223372036854775808
print(Int.max)  // 9223372036854775807

// 如果需要确定大小,使用明确类型
let i8: Int8 = 127      // -128 ~ 127
let i16: Int16 = 32767  // -32768 ~ 32767
let i32: Int32 = 2147483647
let i64: Int64 = 9223372036854775807

2.2 整数溢出

Swift 对溢出的处理非常严格:

swift 复制代码
// Debug 模式下:溢出直接崩溃
let maxInt = Int.max
let overflow = maxInt + 1  // ❌ Crash: arithmetic overflow

// Release 模式:可以配置 wrapping
// 编译器选项:-overflow-checks=false 时会 wrap

如果你需要溢出行为,使用显式方法:

swift 复制代码
let maxInt = Int.max
let wrapped = maxInt &+ 1   // 溢出环绕,不会崩溃
print(wrapped)              // -9223372036854775808

三、String 的深层设计

3.1 String 是值类型

这是 Swift 和 Objective-C 最大的设计差异之一:

swift 复制代码
// Objective-C: NSString 是引用类型
NSString *a = @"Hello";
NSString *b = a;  // b 和 a 指向同一个对象
[a uppercaseString];  // 修改的是同一个对象

// Swift: String 是值类型(Copy-on-Write)
var a = "Hello"
var b = a   // 值拷贝(实际上 COW 优化,只在修改时才真正拷贝)
b += " World"
print(a)   // "Hello" --- a 不受影响
print(b)   // "Hello World"

3.2 字符串的字符计算

Swift 的 count 是 Unicode 感知的,不像其他语言直接返回字节数:

swift 复制代码
let emoji = "👨‍👩‍👧‍👦"
print(emoji.count)        // 1(一个家庭 emoji,由多个 Unicode 码点组成)
print(emoji.utf8.count)   // 25 bytes(4 * 4 + 4 + 4 = 25)

let chinese = "你好世界"
print(chinese.count)      // 4(4 个字符)
print(chinese.utf8.count) // 16(UTF-8 编码)

3.3 字符串的常见操作

swift 复制代码
let str = "Hello, Swift!"

// 长度
print(str.count)                    // 13

// 子串(推荐用 Range,使用下标的已废弃)
let index = str.firstIndex(of: ",")!
let substr = str[..<index]          // "Hello"
let range = str.index(str.startIndex, offsetBy: 5)..<str.endIndex
_ = str[range]                      // ", Swift!"

// 分割
let parts = str.split(separator: ",")  // ["Hello", " Swift!"]
let trimmed = str.trimmingCharacters(in: .whitespaces)

// 查找和替换
if str.contains("Swift") { ... }
let replaced = str.replacingOccurrences(of: "Swift", with: "Objective-C")

// 插值(类型安全)
let name = "Alice"
let age = 25
let greeting = "Hello, \(name). You are \(age) years old."
print(greeting)  // "Hello, Alice. You are 25 years old."

// 多行字符串
let poem = """
    床前明月光,
    疑是地上霜。
    举头望明月,
    低头思故乡。
    """

四、元组:组合多个值

4.1 元组的基础用法

元组是 Swift 特有的类型,用于临时组合少量异构值

swift 复制代码
// 基本元组
let point = (x: 10, y: 20)
print(point.x)  // 10
print(point.y)  // 20

// 索引访问
let first = point.0  // 10
let second = point.1 // 20

// 无标签元组
let rgb = (255, 128, 64)
print(rgb.0)  // 255

// 函数返回多个值
func divide(_ a: Double, by b: Double) -> (quotient: Double, remainder: Double) {
    return (quotient: floor(a / b), remainder: a.truncatingRemainder(dividingBy: b))
}

let result = divide(10.0, by: 3.0)
print("商=\(result.quotient), 余数=\(result.remainder)")

4.2 元组 vs 结构体

场景 使用元组 使用结构体
临时组合 2-3 个值
函数返回多个相关值
作为字典的 Key ✅(需要 Hashable)
需要方法
需要默认值
需要遵守协议
swift 复制代码
// ✅ 临时使用
let httpStatus = (code: 200, message: "OK", isSuccess: true)

// ✅ 作为函数返回类型
func findUser(id: Int) -> (name: String, email: String)? { ... }

// ❌ 长期存储 → 用结构体
struct User {
    let id: Int
    let name: String
    let email: String
}

五、类型别名:让代码更清晰

5.1 基础用法

swift 复制代码
typealias UserID = String
typealias Money = Decimal
typealias CompletionHandler = (Result<Void, Error>) -> Void

// 在代码中使用
func fetchUser(id: UserID) async throws -> User { ... }
let balance: Money = 1000.50

5.2 典型应用场景

swift 复制代码
// 网络层:统一错误类型
enum NetworkError: Error {
    case invalidURL
    case noData
    case decodingFailed
    case serverError(Int)
}
typealias NetworkResult<T> = Result<T, NetworkError>

// 并发:统一线程类型
@MainActor func updateUI() { ... }

// 特定业务:让代码自文档化
typealias Celsius = Double
typealias Fahrenheit = Double

func convert(celsius: Celsius) -> Fahrenheit {
    return celsius * 9 / 5 + 32
}

六、可选类型:Swift 的安全基石

6.1 可选类型的三种表示

swift 复制代码
// 三种写法等价
var name: String? = nil
var name1: Optional<String> = nil
var name2: Optional<String>.none = nil

6.2 解包方法对比

方法 适用场景 安全性
if let / guard let 确认值存在时使用 安全
?? 空值合并 提供默认值 安全
! 强制解包 确定值一定存在(极不推荐) 危险
?. 可选链 安全调用可能为空的方法 安全
swift 复制代码
let optionalName: String? = "Alice"

//// 1. guard let --- 提前退出
func greet(_ name: String?) {
    guard let name else {
        print("No name provided")
        return
    }
    print("Hello, \(name)!")  // name 在此处是非可选的
}

//// 2. if let --- 分支处理
if let name = optionalName {
    print("Hello, \(name)!")
} else {
    print("No name")
}

//// 3. 空值合并 ??
let displayName = optionalName ?? "Guest"

//// 4. 可选链
print(optionalName?.uppercased())  // Optional("ALICE")
print(optionalName?.count)          // Optional(5)

//// 5. 多层可选链
struct Person {
    var address: Address?
}
struct Address {
    var city: City?
}
struct City {
    var name: String
}

// 安全访问深层嵌套属性
let cityName = person.address?.city?.name ?? "Unknown"

6.3 隐式解包的坑

swift 复制代码
// ❌ 危险:value 为 nil 时崩溃
var value: String! = nil
let upper = value.uppercased()  // Crash!

// ✅ 正确:先用 guard 检查
var value: String! = fetchValue()
guard let value else { return }
print(value.uppercased())

七、自定义类型:结构体与类

7.1 结构体(推荐优先使用)

swift 复制代码
struct Point {
    var x: Double
    var y: Double

    // 存储属性
    let origin: Point = Point(x: 0, y: 0)

    // 计算属性
    var distanceFromOrigin: Double {
        sqrt(x * x + y * y)
    }

    // 方法
    func distance(to other: Point) -> Double {
        sqrt(pow(other.x - x, 2) + pow(other.y - y, 2))
    }

    // 静态方法
    static func origin() -> Point {
        Point(x: 0, y: 0)
    }
}

// 用法
let p1 = Point(x: 3, y: 4)
let p2 = Point(x: 6, y: 8)
print(p1.distance(to: p2))  // 5.0

7.2 结构体 vs 类

这是 Swift 中最重要的选择之一:

特性 结构体
类型 值类型(拷贝) 引用类型(共享)
继承 不支持 支持
初始化 编译器自动生成成员初始化器 必须定义 init
身份比较 用 ==(值相等) 用 ===(同一实例)
适用场景 数据载体、轻量对象 需要共享状态、需要继承

Apple 的建议:默认使用结构体,只有在需要引用语义或继承时才用类。

swift 复制代码
// ✅ 默认用结构体
struct User {
    let id: UUID
    var name: String
    var email: String
}

// 只有在需要以下特性时才用类:
// - 引用共享(如单例)
// - 继承
// - deinit 清理资源
class DatabaseManager {
    static let shared = DatabaseManager()  // 单例必须是类
    private init() {}

    func connect() { ... }
}

八、枚举:Swift 最强大的类型之一

8.1 基础枚举

swift 复制代码
enum Direction {
    case north, south, east, west
}

let dir: Direction = .north

switch dir {
case .north: print("北")
case .south: print("南")
case .east:  print("东")
case .west:  print("西")
}

8.2 关联值(最重要!)

枚举可以携带数据,这让枚举成为建模状态的最佳工具:

swift 复制代码
// ❌ 不用枚举的错误方式
class Order {
    var status: String  // "pending", "paid", "shipped", "delivered"
    var trackingNumber: String?  // 只有 shipped/delivered 时才有
    var cancelReason: String?    // 只有 cancelled 时才有
}

// ✅ 用枚举的正确方式
enum OrderStatus {
    case pending
    case paid
    case shipped(trackingNumber: String)
    case delivered
    case cancelled(reason: String)
}

struct Order {
    var status: OrderStatus
    var amount: Decimal
}

let order = Order(
    status: .shipped(trackingNumber: "SF123456789"),
    amount: 99.99
)

// 模式匹配处理
switch order.status {
case .pending:
    print("等待付款")
case .paid:
    print("已付款")
case .shipped(let tracking):
    print("已发货,单号: \(tracking)")
case .delivered:
    print("已送达")
case .cancelled(let reason):
    print("已取消,原因: \(reason)")
}

8.3 原始值与 CaseIterable

swift 复制代码
// 原始值枚举
enum HTTPStatus: Int {
    case ok = 200
    case notFound = 404
    case serverError = 500
}

print(HTTPStatus.ok.rawValue)      // 200
print(HTTPStatus(rawValue: 200)!)   // HTTPStatus.ok

// CaseIterable:枚举所有实例
enum Planet: CaseIterable {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

for planet in Planet.allCases {
    print(planet)
}

九、综合示例:定义一个完整的模型

swift 复制代码
import Foundation

// 货币类型别名
typealias Money = Decimal

// 交易类型枚举
enum TransactionType: String, Codable {
    case income = "income"
    case expense = "expense"
}

// 交易状态枚举
enum TransactionStatus: Equatable {
    case pending
    case completed(date: Date)
    case failed(reason: String)
}

// 交易记录
struct Transaction: Identifiable, Codable {
    let id: UUID
    var type: TransactionType
    var amount: Money
    var category: String
    var note: String?
    var status: TransactionStatus
    let createdAt: Date
    var updatedAt: Date

    // 计算属性
    var displayAmount: String {
        let prefix = type == .income ? "+" : "-"
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencyCode = "CNY"
        return prefix + (formatter.string(from: amount as NSDecimalNumber) ?? "¥0.00")
    }

    // 方法
    func isLargeTransaction(threshold: Money = 1000) -> Bool {
        amount >= threshold
    }

    // 静态工厂方法
    static func income(amount: Money, category: String, note: String? = nil) -> Transaction {
        Transaction(
            id: UUID(),
            type: .income,
            amount: amount,
            category: category,
            note: note,
            status: .pending,
            createdAt: Date(),
            updatedAt: Date()
        )
    }
}

// 使用
let salary = Transaction.income(amount: 15000, category: "工资", note: "4月份工资")
print(salary.displayAmount)  // "+¥15,000.00"

总结

今天我们学习了 Swift 类型系统的核心概念:

  1. 类型安全:编译期检查,消灭大量运行时错误
  2. 类型推导:代码简洁,同时保持类型安全
  3. String 是值类型:Copy-on-Write 优化,线程安全
  4. 元组:临时组合少量异构值
  5. 可选类型:Swift 处理「值可能不存在」的标准方式
  6. 结构体优先:Apple 推荐的默认选择
  7. 枚举关联值:建模状态的最佳工具

下一篇文章我们将深入 Swift 的控制流,看看 guardswitch 的模式匹配、以及 for-in 的各种高级用法。


往期回顾


如果这篇文章对你有帮助,欢迎点赞。你的支持是我持续输出的最大动力。

相关推荐
暗不需求1 小时前
深入理解 React 受控组件与非受控组件:从源码到面试
前端·react.js·面试
Yue1681 小时前
天津理工大学前端组大一末期考核随记(2)
前端·javascript
hexu_blog1 小时前
前端vue后端java+springboot如何实现pdf,word,excel之间的相互转换
java·前端·vue.js·spring boot·文档转换
hyunbar1 小时前
扣子(coze)高级实战-【今日头条】输入关键词批量采集,循环写入飞书多维表格
人工智能·ai编程
w_t_y_y2 小时前
vue父子组件通信(二)祖先调用inject
前端·javascript·vue.js
ftpeak2 小时前
LangGraph Agent 开发指南(10~子图 Subgraphs)
python·ai·langchain·ai编程·langgraph
哆哆啦002 小时前
URL 重写规则和静态资源解析逻辑
前端·浏览器·url
IT_陈寒2 小时前
Java的Stream.peek()千万别乱用,血泪教训
前端·人工智能·后端
w_t_y_y2 小时前
VUE组件配置项(二)data和props
前端·javascript·vue.js