Swift语言基础:从语法到可运行代码的完整认知

Swift 是 Apple 生态中面向 iOS、macOS、watchOS 和 tvOS 应用开发的核心编程语言。它由 Chris Lattner 领导的团队于 2014 年发布,旨在替代 Objective-C,提供更安全、更高效、更易读的编程体验。对于从零开始学习 iOS 开发的学员而言,掌握 Swift 的基本语法不仅是编写 UI 界面的前提,更是理解内存管理、并发编程、协议导向设计等高级主题的基石。本章将系统性地拆解 Swift 的变量与常量、数据类型、运算符、控制流以及函数定义,帮助你建立一套完整的语法认知框架,并规避初学者最常见的编译错误与逻辑陷阱。

Swift 语言基础核心知识结构 Swift 语法基础 变量与常量 var / let / 类型推断 数据类型 Int / Double / String / Bool 运算符 赋值 / 算术 / 比较 / 逻辑 控制流 if / switch / for-in / while 函数 定义 / 参数 / 返回值 / 调用 图:Swift 基础语法五大核心模块及其关联关系

上图展示了本章的知识框架。变量与常量是数据存储的容器,数据类型决定了数据的性质与操作方式,运算符用于加工数据,控制流则让代码具备决策与循环能力,而函数将一系列操作封装为可复用的逻辑单元。这五个模块环环相扣,构成了 Swift 程序的最小可运行单元。下面我们将逐一深入剖析。

核心原理:Swift 的类型安全与类型推断机制

Swift 是一门强类型、静态类型的语言。这意味着每一个变量和常量在编译时必须有明确的类型,且类型一旦确定便不能隐式转换。这种设计极大地减少了运行时因类型不匹配而导致的崩溃。然而,Swift 并不要求开发者总是显式标注类型,它通过类型推断(Type Inference)机制,在声明时根据赋值的字面量自动推导出类型。例如:

let age = 25          // 自动推断为 Int 类型
var price = 19.99     // 自动推断为 Double 类型
let name = "Alice"    // 自动推断为 String 类型
let isActive = true   // 自动推断为 Bool 类型

类型安全还体现在类型转换的严格性上。在 Objective-C 中,你可以将整数赋值给浮点数变量,系统会隐式转换;但在 Swift 中,以下代码会直接报编译错误:

var count: Int = 10
var total: Double = count  // 错误:Cannot assign value of type 'Int' to type 'Double'

正确的做法是显式转换:var total: Double = Double(count)。这种强制转换虽然增加了代码量,但避免了因精度丢失或溢出导致的隐性 bug。

变量与常量:var 与 let 的选择策略

在 Swift 中,使用 var 声明变量(值可修改),使用 let 声明常量(值不可修改)。这不仅是语法习惯,更是代码安全性的重要保障。Apple 官方推荐:优先使用 let,只有在值确实需要变化时才使用 var。原因在于:

  • 可读性:看到 let 就知道该值在整个生命周期内不会改变,减少阅读时的认知负担。
  • 并发安全:常量天然是线程安全的,因为不可变对象无需加锁。
  • 编译器优化:编译器可以对常量进行更积极的优化,提升运行效率。

来看一个实际场景:

let maxLoginAttempts = 3
var currentAttempts = 0

func login() {
    if currentAttempts < maxLoginAttempts {
        currentAttempts += 1
        print("登录中...")
    } else {
        print("尝试次数已用尽")
    }
}

在这个例子中,maxLoginAttempts 是业务规则,不应被修改,因此用 let;currentAttempts 是状态,需要递增,因此用 var。这种清晰的区分让代码的意图一目了然。

数据类型:从基础类型到集合类型

Swift 的基础数据类型包括:

类型说明示例内存占用(64位系统)
Int整数,自动适配平台位数let n = 428 字节
Double64位浮点数,精度高let pi = 3.141598 字节
Float32位浮点数,精度较低let f: Float = 3.144 字节
Bool布尔值,true/falselet flag = false1 字节
String字符串,值类型let s = "Hello"动态
Character单个字符let c: Character = "A"动态

除了基础类型,Swift 还提供了强大的集合类型:Array(有序数组)、Set(无序无重复集合)、Dictionary(键值对字典)。这些集合类型都是值类型,意味着它们在赋值或传递时会进行拷贝,而不是引用共享。这在多线程环境中尤其重要。

var fruits: [String] = ["Apple", "Banana", "Orange"]
var uniqueNumbers: Set<Int> = [1, 2, 3, 3, 2]  // 实际为 {1, 2, 3}
var capitals: [String: String] = ["China": "Beijing", "Japan": "Tokyo"]

注意,数组和字典在使用时需要注意可选性。例如,通过下标访问字典时,返回的是一个可选值:

let capitalOfChina = capitals["China"]  // 类型为 String?,值为 Optional("Beijing")
let capitalOfFrance = capitals["France"] // 类型为 String?,值为 nil

这种设计迫使开发者处理键不存在的情况,从而避免空指针异常。

运算符:不仅仅是加减乘除

Swift 的运算符分为以下几类:

  1. 赋值运算符:=,以及复合赋值 +=、-=、*= 等。
  2. 算术运算符:+、-、*、/、%(取余)。注意,整数除法会截断小数部分。
  3. 比较运算符:==、!=、>、<、>=、<=,返回 Bool 值。
  4. 逻辑运算符:&&(与)、||(或)、!(非)。
  5. 范围运算符:...(闭区间)、..<(半开区间),常用于循环和数组切片。
  6. 三元运算符:condition ? value1 : value2,简洁的条件赋值。

一个容易混淆的点是 % 运算符对负数的处理。在 Swift 中,-7 % 3 的结果是 -1,因为结果的符号跟随被除数。这与 Python 不同(Python 中结果为 2)。在金融计算或循环索引时,务必注意这一点。

另外,Swift 支持运算符重载,你可以为自定义类型定义 +、== 等运算符的行为。例如,为坐标结构体实现加法:

struct Point {
    var x: Int
    var y: Int
    static func + (left: Point, right: Point) -> Point {
        return Point(x: left.x + right.x, y: left.y + right.y)
    }
}
let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 3, y: 4)
let p3 = p1 + p2  // Point(x: 4, y: 6)

控制流:if、switch 与循环的实战运用

控制流是程序逻辑的骨架。Swift 的 if 语句要求条件必须是 Bool 类型,这避免了 C 语言中非零即真的隐式转换。例如:

let score = 85
if score >= 90 {
    print("优秀")
} else if score >= 60 {
    print("及格")
} else {
    print("不及格")
}

switch 语句在 Swift 中远比 C 语言强大。它不需要显式 break,每个分支执行完自动退出;且支持区间匹配、元组匹配、值绑定和 where 条件过滤。例如:

let grade = "B"
switch grade {
case "A":
    print("90-100分")
case "B", "C":
    print("60-89分")
case "D":
    print("不及格")
default:
    print("无效成绩")
}

循环方面,Swift 提供 for-in、while 和 repeat-while。其中 for-in 是最常用的,可以遍历区间、数组、字典等。注意,如果你不需要使用循环变量,可以用下划线 _ 代替:

for _ in 1...5 {
    print("执行5次")
}

let names = ["Alice", "Bob", "Charlie"]
for name in names {
    print("Hello, \(name)")
}

在循环中,break 用于提前终止整个循环,continue 用于跳过当前迭代。对于嵌套循环,可以使用标签(label)来指定要跳出或继续的循环:

outerLoop: for i in 1...3 {
    for j in 1...3 {
        if i == 2 && j == 2 {
            break outerLoop  // 跳出外层循环
        }
        print("i=\(i), j=\(j)")
    }
}

函数:定义、参数与返回值

函数是代码复用的最小单元。Swift 函数的定义语法如下:

func 函数名(参数标签 参数名: 类型) -> 返回类型 {
    // 函数体
    return 返回值
}

Swift 函数的参数系统非常灵活,包含以下特性:

  • 参数标签:每个参数可以有两个名称——外部标签(调用时使用)和内部名称(函数体内使用)。如果不想让调用者看到标签,可以用下划线 _ 省略。
  • 默认参数值:可以为参数指定默认值,调用时可省略。
  • 可变参数:接受不确定数量的同类型参数,用 ... 表示。
  • 输入输出参数(inout):允许函数修改外部变量的值,类似于指针传递。

来看一个综合示例:

func calculateTotal(price: Double, quantity: Int, discount: Double = 0.0) -> Double {
    let subtotal = price * Double(quantity)
    let discounted = subtotal * (1 - discount)
    return discounted
}

// 调用方式
let total1 = calculateTotal(price: 99.0, quantity: 2)
let total2 = calculateTotal(price: 99.0, quantity: 2, discount: 0.1)

函数也可以作为参数传递或作为返回值,这被称为函数类型。例如:

func add(_ a: Int, _ b: Int) -> Int { a + b }
func multiply(_ a: Int, _ b: Int) -> Int { a * b }

func applyOperation(_ op: (Int, Int) -> Int, _ x: Int, _ y: Int) -> Int {
    return op(x, y)
}

let result = applyOperation(add, 5, 3)      // 8
let result2 = applyOperation(multiply, 5, 3) // 15

这种高阶函数的特性,为后续学习闭包(Closure)和函数式编程奠定了基础。

案例拆解:构建一个简易的购物车结算系统

为了将上述知识点串联起来,我们设计一个简易的购物车结算场景。假设一个电商 App 需要计算购物车中商品的总价,并应用会员折扣和满减优惠。

需求约束:

  • 商品包含名称、单价和数量。
  • 会员折扣:普通会员 95 折,VIP 会员 8 折。
  • 满减规则:总价满 200 元减 20 元,满 500 元减 80 元。
  • 结算结果需要显示原价、优惠明细和最终应付金额。

代码实现:

struct Product {
    let name: String
    let price: Double
    let quantity: Int
}

enum Membership {
    case normal, silver, vip
}

func calculateCartTotal(products: [Product], membership: Membership) -> (original: Double, discount: Double, final: Double) {
    // 1. 计算原价
    var originalTotal = 0.0
    for product in products {
        originalTotal += product.price * Double(product.quantity)
    }
    
    // 2. 会员折扣
    var discountRate = 1.0
    switch membership {
    case .normal:
        discountRate = 1.0
    case .silver:
        discountRate = 0.95
    case .vip:
        discountRate = 0.80
    }
    let afterMemberDiscount = originalTotal * discountRate
    
    // 3. 满减优惠
    var reduction = 0.0
    if afterMemberDiscount >= 500 {
        reduction = 80.0
    } else if afterMemberDiscount >= 200 {
        reduction = 20.0
    }
    
    let finalTotal = afterMemberDiscount - reduction
    let totalDiscount = originalTotal - finalTotal
    
    return (originalTotal, totalDiscount, finalTotal)
}

// 测试数据
let cart = [
    Product(name: "iPhone 15", price: 5999.0, quantity: 1),
    Product(name: "AirPods Pro", price: 1899.0, quantity: 1),
    Product(name: "充电器", price: 149.0, quantity: 2)
]

let result = calculateCartTotal(products: cart, membership: .vip)
print("原价:\(result.original) 元")
print("总优惠:\(result.discount) 元")
print("应付:\(result.final) 元")

运行结果:

原价:8196.0 元
总优惠:1639.2 元
应付:6556.8 元

复盘分析:这个案例综合运用了结构体(数据类型)、枚举(数据类型)、数组(集合类型)、for-in 循环(控制流)、switch(控制流)、if-else(控制流)、函数定义与元组返回值。通过这个案例,你可以看到基础语法如何协同工作来解决实际问题。值得注意的是,originalTotal 使用了 var 因为需要累加;而 discountRate 和 reduction 使用了 let 因为一旦计算完成就不再变化。这种选择体现了 Swift 的编程哲学。

常见误区与总结复盘

初学者在学习 Swift 基础语法时,常犯以下错误:

误区一:混淆 var 与 let 的使用场景
有些学员习惯将所有声明都写成 var,认为这样更灵活。但这样做不仅降低了代码可读性,还可能在多线程环境中引入数据竞争。正确的做法是:默认使用 let,只有当值确实需要被修改时才改用 var。
误区二:忽略类型转换的显式性
在 Swift 中,Int 和 Double 之间的运算必须显式转换。例如 let result = 3 + 0.14 会报错,必须写成 let result = Double(3) + 0.14。很多初学者在混合运算时容易忽略这一点。
误区三:switch 语句忘记处理 default 分支
虽然 Swift 的 switch 不需要显式 break,但如果枚举或整数类型没有覆盖所有可能情况,编译器会强制要求添加 default 分支。否则代码无法编译通过。
经验建议:在 Xcode 中编写代码时,充分利用编译器的错误提示。Swift 编译器非常智能,它会给出具体的修复建议。例如,当你尝试将 Int 赋值给 Double 时,编译器会提示 "Fix-it: Insert Double()"。多阅读这些提示,能帮助你快速建立类型安全的直觉。

总结本章,Swift 的基础语法虽然看似简单,但每一个细节都体现了语言设计者对安全性和表达力的追求。变量与常量的选择、类型的严格管理、控制流的灵活性、函数的参数体系,这些都不是孤立的语法点,而是相互关联的整体。建议你在学习过程中,多动手编写小实验代码,例如修改购物车案例中的折扣规则,或者添加新的商品类型,通过实际运行来验证你的理解。只有将语法内化为思维习惯,才能在后续的 UI 开发、网络请求、数据持久化等高级主题中游刃有余。