Swift 語法學習指南 - 與 Kotlin 對比
本指南專為有 Android/Kotlin 開發經驗的開發者設計,通過對比學習快速掌握 Swift 語法
目錄
- 語言基礎對比
- 變量與常量
- 數據類型
- 函數定義
- 類與結構體
- 繼承與協議
- 可選類型
- 集合類型
- 控制流
- 閉包與Lambda
- 擴展與Extension
- 錯誤處理
- 內存管理
- 實戰練習
語言基礎對比
基本特性對比
特性 | Swift | Kotlin |
---|---|---|
類型推斷 | ? 強類型推斷 | ? 強類型推斷 |
空安全 | ? Optional 類型 | ? Nullable 類型 |
函數式編程 | ? 支持 | ? 支持 |
面向對象 | ? 支持 | ? 支持 |
協議/接口 | Protocol | Interface |
擴展 | Extension | Extension Function |
變量與常量
Swift
// 常量 - 不可變
let name = "張三" // 類型推斷
let age: Int = 25 // 顯式類型// 變量 - 可變
var score = 90
var height: Double = 175.5// 延遲初始化
lazy var expensiveResource = createResource()
Kotlin
// 常量 - 不可變
val name = "張三" // 類型推斷
val age: Int = 25 // 顯式類型// 變量 - 可變
var score = 90
var height: Double = 175.5// 延遲初始化
val expensiveResource by lazy { createResource() }
對比總結
- Swift 使用
let
/var
,Kotlin 使用val
/var
- 兩者都支持類型推斷
- Swift 的
lazy
是關鍵字,Kotlin 使用by lazy
委托
數據類型
基本數據類型對比
類型 | Swift | Kotlin |
---|---|---|
整數 | Int , Int8 , Int16 , Int32 , Int64 | Int , Byte , Short , Int , Long |
浮點 | Float , Double | Float , Double |
布爾 | Bool | Boolean |
字符 | Character | Char |
字符串 | String | String |
字符串操作
Swift
// 字符串插值
let name = "Swift"
let message = "Hello, \(name)!"// 多行字符串
let multiline = """
這是一個
多行字符串
"""// 字符串方法
let text = "Hello World"
print(text.count) // 長度
print(text.uppercased()) // 大寫
print(text.contains("World")) // 包含
Kotlin
// 字符串插值
val name = "Kotlin"
val message = "Hello, $name!"
val complexMessage = "Hello, ${name.uppercase()}!"// 多行字符串
val multiline = """這是一個多行字符串
""".trimIndent()// 字符串方法
val text = "Hello World"
println(text.length) // 長度
println(text.uppercase()) // 大寫
println(text.contains("World")) // 包含
協議屬性聲明詳解
為什么協議中必須使用 var
而不是 let
?
Swift 協議中的屬性聲明有特殊的語法規則:
1. 協議屬性聲明規則
protocol MyProtocol {// ? 正確:只讀屬性var readOnlyProperty: String { get }// ? 正確:可讀寫屬性var readWriteProperty: Int { get set }// ? 錯誤:協議中不能使用 let// let constantProperty: String { get } // 編譯錯誤
}
2. 實現協議時的靈活性
class MyClass: MyProtocol {// 只讀屬性可以用 let 實現let readOnlyProperty: String = "Hello"// 也可以用 var 實現var anotherReadOnly: String {return "World"}// 可讀寫屬性必須用 var 實現var readWriteProperty: Int = 42
}
3. 與 Kotlin 的對比
特性 | Swift 協議 | Kotlin 接口 |
---|---|---|
只讀屬性聲明 | var property: Type { get } | val property: Type |
可讀寫屬性聲明 | var property: Type { get set } | var property: Type |
實現只讀屬性 | 可用 let 或 var | 可用 val 或 var |
實現可讀寫屬性 | 必須用 var | 必須用 var |
4. 核心要點
- 協議聲明:Swift 協議中所有屬性都必須用
var
聲明 - 訪問控制:通過
{ get }
和{ get set }
指定屬性的訪問權限 - 實現靈活性:只讀屬性(
{ get }
)在實現時可以是let
常量 - 類型安全:編譯器確保實現類滿足協議的訪問權限要求
這種設計讓 Swift 協議既保持了靈活性,又確保了類型安全。
函數定義
Swift
// 基本函數
func greet(name: String) -> String {return "Hello, \(name)!"
}// 帶默認參數
func greet(name: String, age: Int = 18) -> String {return "Hello, \(name), you are \(age) years old"
}// 可變參數
func sum(_ numbers: Int...) -> Int {return numbers.reduce(0, +)
}// 高階函數
func processData(_ data: [Int], processor: (Int) -> Int) -> [Int] {return data.map(processor)
}// 調用
let result1 = greet(name: "張三")
let result2 = greet(name: "李四", age: 25)
let total = sum(1, 2, 3, 4, 5)
Kotlin
// 基本函數
fun greet(name: String): String {return "Hello, $name!"
}// 帶默認參數
fun greet(name: String, age: Int = 18): String {return "Hello, $name, you are $age years old"
}// 可變參數
fun sum(vararg numbers: Int): Int {return numbers.sum()
}// 高階函數
fun processData(data: List<Int>, processor: (Int) -> Int): List<Int> {return data.map(processor)
}// 調用
val result1 = greet("張三")
val result2 = greet("李四", 25)
val total = sum(1, 2, 3, 4, 5)
對比總結
- Swift 使用
func
關鍵字,Kotlin 使用fun
- Swift 參數標簽更靈活,Kotlin 相對簡單
- Swift 可變參數用
...
,Kotlin 用vararg
類與結構體
Swift
// 結構體(值類型)
struct Point {var x: Doublevar y: Double// 計算屬性var distance: Double {return sqrt(x * x + y * y)}// 方法// mutating 關鍵字說明:// 在 Swift 中,結構體(struct)是值類型,默認情況下其方法不能修改實例的屬性// 如果需要在方法中修改結構體的屬性,必須使用 mutating 關鍵字標記該方法// 這是 Swift 的安全機制,確保值類型的不可變性,除非明確聲明為可變mutating func moveBy(x deltaX: Double, y deltaY: Double) {x += deltaX // 修改實例屬性 xy += deltaY // 修改實例屬性 y}
}// 類(引用類型)
class Person {var name: Stringvar age: Int// 構造器init(name: String, age: Int) {self.name = nameself.age = age}// 方法func introduce() -> String {return "我是\(name),今年\(age)歲"}
}// 使用
var point = Point(x: 3.0, y: 4.0)
print(point.distance) // 5.0
point.moveBy(x: 1.0, y: 1.0)let person = Person(name: "張三", age: 25)
print(person.introduce())
Kotlin
// 數據類(類似結構體)
data class Point(var x: Double,var y: Double
) {// 計算屬性val distance: Doubleget() = sqrt(x * x + y * y)// 方法fun moveBy(deltaX: Double, deltaY: Double) {x += deltaXy += deltaY}
}// 類
class Person(var name: String,var age: Int
) {// 方法fun introduce(): String {return "我是$name,今年${age}歲"}
}// 使用
val point = Point(3.0, 4.0)
println(point.distance) // 5.0
point.moveBy(1.0, 1.0)val person = Person("張三", 25)
println(person.introduce())
mutating 關鍵字詳解
Swift 中的 mutating
mutating
是 Swift 中一個重要的關鍵字,專門用于值類型(struct 和 enum):
struct Counter {var count = 0// ? 錯誤:不能在非 mutating 方法中修改屬性// func increment() {// count += 1 // 編譯錯誤// }// ? 正確:使用 mutating 關鍵字mutating func increment() {count += 1 // 可以修改屬性}mutating func reset() {count = 0}// 不修改屬性的方法不需要 mutatingfunc getCurrentCount() -> Int {return count}
}// 使用示例
var counter = Counter() // 注意:必須是 var,不能是 let
counter.increment() // count = 1
counter.increment() // count = 2
print(counter.getCurrentCount()) // 2// let counter2 = Counter()
// counter2.increment() // ? 編譯錯誤:let 常量不能調用 mutating 方法
與 Kotlin 的對比
特性 | Swift | Kotlin |
---|---|---|
值類型可變性 | 需要 mutating 關鍵字明確標記 | 直接修改,無需特殊標記 |
編譯時檢查 | 嚴格檢查,防止意外修改 | 相對寬松 |
常量實例 | let 實例不能調用 mutating 方法 | val 實例仍可修改內部可變屬性 |
安全性 | 更高的類型安全性 | 依賴開發者自覺 |
為什么需要 mutating?
- 值類型的不可變性:Swift 的 struct 是值類型,默認不可變
- 明確的意圖表達:
mutating
明確表示該方法會修改實例 - 編譯時安全:防止意外修改,提高代碼安全性
- 函數式編程支持:鼓勵不可變編程風格
實際應用場景
struct BankAccount {private var balance: Doubleinit(initialBalance: Double) {self.balance = initialBalance}// 查詢余額 - 不需要 mutatingfunc getBalance() -> Double {return balance}// 存款 - 需要 mutatingmutating func deposit(amount: Double) {guard amount > 0 else { return }balance += amount}// 取款 - 需要 mutatingmutating func withdraw(amount: Double) -> Bool {guard amount > 0 && amount <= balance else {return false}balance -= amountreturn true}
}var account = BankAccount(initialBalance: 1000.0)
print(account.getBalance()) // 1000.0
account.deposit(amount: 500.0)
print(account.getBalance()) // 1500.0
let success = account.withdraw(amount: 200.0)
print(account.getBalance()) // 1300.0
繼承與協議
Swift
// 協議定義
protocol Drawable {func draw()// 注意:協議中的屬性聲明必須使用 var,不能使用 let// { get } 表示這是一個只讀屬性,實現時可以是 let 常量或 var 變量// 但在協議聲明中必須用 var,因為協議不知道具體實現方式var area: Double { get }
}protocol Colorable {// { get set } 表示這是一個可讀寫屬性,實現時必須是 var 變量var color: String { get set }
}// 基類
class Shape: Drawable {var area: Double {return 0.0}func draw() {print("繪制形狀")}
}// 繼承和協議實現
class Circle: Shape, Colorable {var radius: Doublevar color: Stringinit(radius: Double, color: String) {self.radius = radiusself.color = colorsuper.init()}override var area: Double {return Double.pi * radius * radius}override func draw() {print("繪制\(color)的圓形")}
}
Kotlin
// 接口定義
interface Drawable {fun draw()val area: Double
}interface Colorable {var color: String
}// 基類
open class Shape : Drawable {override val area: Doubleget() = 0.0override fun draw() {println("繪制形狀")}
}// 繼承和接口實現
class Circle(val radius: Double,override var color: String
) : Shape(), Colorable {override val area: Doubleget() = Math.PI * radius * radiusoverride fun draw() {println("繪制${color}的圓形")}
}
可選類型
Swift Optional
// 可選類型聲明
var optionalName: String? = "張三"
var optionalAge: Int? = nil// 可選綁定
if let name = optionalName {// \(name) 是字符串插值,name 是變量調用// 在字符串插值中可以調用屬性、方法和擴展函數print("姓名是: \(name)")print("姓名長度: \(name.count)") // 調用屬性print("大寫姓名: \(name.uppercased())") // 調用方法
} else {print("姓名為空")
}// guard 語句
func processUser(name: String?) {guard let userName = name else {print("用戶名不能為空")return}print("處理用戶: \(userName)")
}// 空合并運算符
let displayName = optionalName ?? "未知用戶"// 強制解包(危險操作)
let forcedName = optionalName! // 如果為nil會崩潰// 可選鏈
class Person {var address: Address?
}class Address {var street: String?
}let person: Person? = Person()
let street = person?.address?.street // 可選鏈調用
Kotlin Nullable
// 可空類型聲明
var optionalName: String? = "張三"
var optionalAge: Int? = null// 安全調用
optionalName?.let { name ->println("姓名是: $name")
} ?: run {println("姓名為空")
}// Elvis 運算符
val displayName = optionalName ?: "未知用戶"// 非空斷言(危險操作)
val forcedName = optionalName!! // 如果為null會拋異常// 安全調用鏈
class Person {var address: Address? = null
}class Address {var street: String? = null
}val person: Person? = Person()
val street = person?.address?.street // 安全調用鏈
對比總結
- Swift 使用
?
和!
,Kotlin 也使用?
和!!
- Swift 的
if let
對應 Kotlin 的?.let
- Swift 的
??
對應 Kotlin 的?:
字符串插值與擴展函數調用
回答你的問題:是的,\(name)
是對變量的調用,而且在字符串插值中可以調用屬性、方法和擴展函數。
Swift 字符串插值
let name = "張三"
let age = 25// 基本變量調用
print("姓名: \(name)")// 調用屬性和方法
print("姓名長度: \(name.count)")
print("大寫姓名: \(name.uppercased())")
print("年齡加10: \(age + 10)")// 調用擴展函數
extension String {func addPrefix(_ prefix: String) -> String {return prefix + self}
}print("帶前綴的姓名: \(name.addPrefix("Mr. "))")// 復雜表達式
print("信息: \(name.isEmpty ? "無名" : name.uppercased())")
Kotlin 字符串模板
val name = "張三"
val age = 25// 基本變量調用
println("姓名: $name")// 調用屬性和方法
println("姓名長度: ${name.length}")
println("大寫姓名: ${name.uppercase()}")
println("年齡加10: ${age + 10}")// 調用擴展函數
fun String.addPrefix(prefix: String): String {return prefix + this
}println("帶前綴的姓名: ${name.addPrefix("Mr. ")}")// 復雜表達式
println("信息: ${if (name.isEmpty()) "無名" else name.uppercase()}")
對比總結
特性 | Swift | Kotlin |
---|---|---|
基本語法 | \(variable) | $variable |
復雜表達式 | \(expression) | ${expression} |
方法調用 | \(obj.method()) | ${obj.method()} |
擴展函數 | \(obj.extensionFunc()) | ${obj.extensionFunc()} |
條件表達式 | \(condition ? a : b) | ${if (condition) a else b} |
核心要點
- 變量調用:字符串插值中的變量是正常的變量訪問
- 方法鏈式調用:可以在插值中進行鏈式方法調用
- 擴展函數支持:完全支持調用擴展函數
- 表達式計算:插值內可以進行復雜的表達式計算
- 類型安全:編譯時檢查確保類型正確
集合類型
Swift
// 數組
var numbers = [1, 2, 3, 4, 5]
var strings: [String] = ["a", "b", "c"]// 數組操作
numbers.append(6)
numbers.insert(0, at: 0)
let first = numbers.first // Optional<Int>
let count = numbers.count// 字典
var scores = ["張三": 95, "李四": 87]
var ages: [String: Int] = [:]// 字典操作
scores["王五"] = 92
let zhangScore = scores["張三"] // Optional<Int>// 集合
var uniqueNumbers: Set<Int> = [1, 2, 3, 3, 4] // {1, 2, 3, 4}// 高階函數
let doubled = numbers.map { $0 * 2 }
let evens = numbers.filter { $0 % 2 == 0 }
let sum = numbers.reduce(0, +)
Kotlin
// 列表
val numbers = mutableListOf(1, 2, 3, 4, 5)
val strings: List<String> = listOf("a", "b", "c")// 列表操作
numbers.add(6)
numbers.add(0, 0)
val first = numbers.firstOrNull() // Int?
val count = numbers.size// 映射
val scores = mutableMapOf("張三" to 95, "李四" to 87)
val ages: MutableMap<String, Int> = mutableMapOf()// 映射操作
scores["王五"] = 92
val zhangScore = scores["張三"] // Int?// 集合
val uniqueNumbers: Set<Int> = setOf(1, 2, 3, 3, 4) // {1, 2, 3, 4}// 高階函數
val doubled = numbers.map { it * 2 }
val evens = numbers.filter { it % 2 == 0 }
val sum = numbers.reduce { acc, n -> acc + n }
控制流
Swift
// if-else
let score = 85
if score >= 90 {print("優秀")
} else if score >= 80 {print("良好")
} else {print("需要努力")
}// switch
let grade = "A"
switch grade {case "A":print("優秀")case "B", "C":print("良好")case "D":print("及格")default:print("不及格")
}// for 循環
for i in 1...5 {print(i)
}for i in 1..<5 {print(i) // 1, 2, 3, 4
}for (index, value) in ["a", "b", "c"].enumerated() {print("\(index): \(value)")
}// while 循環
var count = 0
while count < 5 {print(count)count += 1
}
Kotlin
// if-else
val score = 85
if (score >= 90) {println("優秀")
} else if (score >= 80) {println("良好")
} else {println("需要努力")
}// when (類似 switch)
val grade = "A"
when (grade) {"A" -> println("優秀")"B", "C" -> println("良好")"D" -> println("及格")else -> println("不及格")
}// for 循環
for (i in 1..5) {println(i)
}for (i in 1 until 5) {println(i) // 1, 2, 3, 4
}for ((index, value) in listOf("a", "b", "c").withIndex()) {println("$index: $value")
}// while 循環
var count = 0
while (count < 5) {println(count)count++
}
閉包與Lambda
Swift 閉包
// 基本閉包語法
let numbers = [1, 2, 3, 4, 5]// 完整語法
let doubled1 = numbers.map({ (number: Int) -> Int inreturn number * 2
})// 簡化語法
let doubled2 = numbers.map { number inreturn number * 2
}// 最簡語法
let doubled3 = numbers.map { $0 * 2 }// 尾隨閉包
let filtered = numbers.filter { $0 % 2 == 0 }// 捕獲值
func makeIncrementer(incrementAmount: Int) -> () -> Int {var total = 0let incrementer: () -> Int = {total += incrementAmountreturn total}return incrementer
}let incrementByTwo = makeIncrementer(incrementAmount: 2)
print(incrementByTwo()) // 2
print(incrementByTwo()) // 4
Kotlin Lambda
// 基本 Lambda 語法
val numbers = listOf(1, 2, 3, 4, 5)// 完整語法
val doubled1 = numbers.map({ number: Int -> number * 2 })// 簡化語法
val doubled2 = numbers.map { number -> number * 2 }// 最簡語法(使用 it)
val doubled3 = numbers.map { it * 2 }// 過濾
val filtered = numbers.filter { it % 2 == 0 }// 捕獲值
fun makeIncrementer(incrementAmount: Int): () -> Int {var total = 0return {total += incrementAmounttotal}
}val incrementByTwo = makeIncrementer(2)
println(incrementByTwo()) // 2
println(incrementByTwo()) // 4
擴展與Extension
Swift Extension
// 擴展基本類型
extension String {var isEmail: Bool {return self.contains("@") && self.contains(".")}func reversed() -> String {return String(self.reversed())}
}// 擴展自定義類型
extension Person {func greet() -> String {return "Hello, I'm \(name)"}var isAdult: Bool {return age >= 18}
}// 使用擴展
let email = "test@example.com"
print(email.isEmail) // true
print("Swift".reversed()) // tfiwSlet person = Person(name: "張三", age: 20)
print(person.greet())
print(person.isAdult)
Kotlin Extension
// 擴展基本類型
val String.isEmail: Booleanget() = this.contains("@") && this.contains(".")fun String.reversed(): String {return this.reversed()
}// 擴展自定義類型
fun Person.greet(): String {return "Hello, I'm $name"
}val Person.isAdult: Booleanget() = age >= 18// 使用擴展
val email = "test@example.com"
println(email.isEmail) // true
println("Kotlin".reversed()) // niltoKval person = Person("張三", 20)
println(person.greet())
println(person.isAdult)
錯誤處理
Swift 錯誤處理
// 定義錯誤類型
enum ValidationError: Error {case emptyNamecase invalidAgecase invalidEmail
}// 可能拋出錯誤的函數
func validateUser(name: String, age: Int, email: String) throws -> Bool {if name.isEmpty {throw ValidationError.emptyName}if age < 0 || age > 150 {throw ValidationError.invalidAge}if !email.contains("@") {throw ValidationError.invalidEmail}return true
}// 錯誤處理
do {try validateUser(name: "張三", age: 25, email: "zhang@example.com")print("用戶驗證成功")
} catch ValidationError.emptyName {print("姓名不能為空")
} catch ValidationError.invalidAge {print("年齡無效")
} catch ValidationError.invalidEmail {print("郵箱格式無效")
} catch {print("未知錯誤: \(error)")
}// try? 和 try!
let result1 = try? validateUser(name: "李四", age: 30, email: "li@example.com")
let result2 = try! validateUser(name: "王五", age: 28, email: "wang@example.com")
Kotlin 異常處理
// 自定義異常
class ValidationException(message: String) : Exception(message)// 可能拋出異常的函數
fun validateUser(name: String, age: Int, email: String): Boolean {if (name.isEmpty()) {throw ValidationException("姓名不能為空")}if (age < 0 || age > 150) {throw ValidationException("年齡無效")}if (!email.contains("@")) {throw ValidationException("郵箱格式無效")}return true
}// 異常處理
try {validateUser("張三", 25, "zhang@example.com")println("用戶驗證成功")
} catch (e: ValidationException) {println("驗證錯誤: ${e.message}")
} catch (e: Exception) {println("未知錯誤: ${e.message}")
}// runCatching (類似 try?)
val result = runCatching {validateUser("李四", 30, "li@example.com")
}.getOrNull()
內存管理
Swift ARC (自動引用計數)
class Person {let name: Stringvar apartment: Apartment?init(name: String) {self.name = name}deinit {print("\(name) 被釋放")}
}class Apartment {let unit: Stringweak var tenant: Person? // 弱引用避免循環引用init(unit: String) {self.unit = unit}deinit {print("公寓 \(unit) 被釋放")}
}// 使用
var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")john?.apartment = unit4A
unit4A?.tenant = john// 釋放引用 - Swift ARC 需要手動打破循環引用
// 如果不手動置 nil,即使使用了 weak 引用,外部強引用仍然存在
john = nil // 釋放 Person 實例的強引用
unit4A = nil // 釋放 Apartment 實例的強引用
Kotlin GC (垃圾回收)
class Person(val name: String) {var apartment: Apartment? = nullprotected fun finalize() {println("$name 被回收")}
}class Apartment(val unit: String) {var tenant: Person? = null // Kotlin 的 GC 會處理循環引用protected fun finalize() {println("公寓 $unit 被回收")}
}// 使用
var john: Person? = Person("John")
var unit4A: Apartment? = Apartment("4A")john?.apartment = unit4A
unit4A?.tenant = john// 釋放引用 - Kotlin GC 可以自動處理循環引用
// 即使不手動置 null,GC 也能檢測并回收循環引用的對象
john = null // 可選操作,GC 會自動處理
unit4A = null // 可選操作,GC 會自動處理
Swift ARC vs Kotlin GC 對比
回答你的問題:你說得對!Kotlin 確實不需要手動置 null,而 Swift 在某些情況下需要。
內存管理機制對比
特性 | Swift ARC | Kotlin GC |
---|---|---|
管理方式 | 自動引用計數 | 垃圾回收器 |
循環引用處理 | 需要 weak/unowned 引用 | 自動檢測和處理 |
手動置 nil | 有時需要 | 通常不需要 |
性能特點 | 確定性釋放,低延遲 | 可能有 GC 暫停 |
內存泄漏風險 | 循環引用導致泄漏 | 很少發生 |
具體差異說明
Swift ARC 的限制:
// 即使使用了 weak 引用,外部強引用仍需手動釋放
var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")john?.apartment = unit4A
unit4A?.tenant = john // weak 引用// 必須手動置 nil,否則 john 和 unit4A 不會被釋放
john = nil // 必需!
unit4A = nil // 必需!
Kotlin GC 的優勢:
// GC 可以自動檢測循環引用
var john: Person? = Person("John")
var unit4A: Apartment? = Apartment("4A")john?.apartment = unit4A
unit4A?.tenant = john // 普通引用// 不需要手動置 null,GC 會自動處理
// john = null // 可選
// unit4A = null // 可選// 當這些變量超出作用域時,GC 會自動回收
核心要點
- Swift:ARC 基于引用計數,無法自動處理循環引用,需要開發者使用
weak
/unowned
和手動置nil
- Kotlin:GC 可以檢測并自動回收循環引用的對象,開發者無需特殊處理
- 最佳實踐:Swift 中養成手動置
nil
的習慣,Kotlin 中可以依賴 GC 自動管理
這就是為什么 Swift 開發者需要更加注意內存管理,而 Kotlin/Java 開發者相對輕松一些。
實戰練習
練習1:創建一個簡單的學生管理系統
Swift 版本
struct Student {let id: Stringvar name: Stringvar age: Intvar scores: [String: Double] // 科目: 分數var averageScore: Double {guard !scores.isEmpty else { return 0.0 }return scores.values.reduce(0, +) / Double(scores.count)}mutating func addScore(subject: String, score: Double) {scores[subject] = score}
}class StudentManager {private var students: [String: Student] = [:]func addStudent(_ student: Student) {students[student.id] = student}func getStudent(id: String) -> Student? {return students[id]}func getAllStudents() -> [Student] {return Array(students.values)}func getTopStudents(count: Int) -> [Student] {return getAllStudents().sorted { $0.averageScore > $1.averageScore }.prefix(count).map { $0 }}
}// 使用示例
let manager = StudentManager()var student1 = Student(id: "001", name: "張三", age: 20, scores: [:])
student1.addScore(subject: "數學", score: 95.0)
student1.addScore(subject: "英語", score: 87.0)manager.addStudent(student1)if let student = manager.getStudent(id: "001") {print("學生: \(student.name), 平均分: \(student.averageScore)")
}
Kotlin 版本
data class Student(val id: String,var name: String,var age: Int,private val scores: MutableMap<String, Double> = mutableMapOf()
) {val averageScore: Doubleget() = if (scores.isEmpty()) 0.0 else scores.values.average()fun addScore(subject: String, score: Double) {scores[subject] = score}fun getScores(): Map<String, Double> = scores.toMap()
}class StudentManager {private val students = mutableMapOf<String, Student>()fun addStudent(student: Student) {students[student.id] = student}fun getStudent(id: String): Student? {return students[id]}fun getAllStudents(): List<Student> {return students.values.toList()}fun getTopStudents(count: Int): List<Student> {return getAllStudents().sortedByDescending { it.averageScore }.take(count)}
}// 使用示例
val manager = StudentManager()val student1 = Student("001", "張三", 20)
student1.addScore("數學", 95.0)
student1.addScore("英語", 87.0)manager.addStudent(student1)manager.getStudent("001")?.let { student ->println("學生: ${student.name}, 平均分: ${student.averageScore}")
}
練習2:網絡請求封裝
Swift 版本
import Foundationenum NetworkError: Error {case invalidURLcase noDatacase decodingError
}class NetworkManager {static let shared = NetworkManager()private init() {}func request<T: Codable>(url: String,type: T.Type,completion: @escaping (Result<T, NetworkError>) -> Void) {// guard 語句:提前退出模式,確保條件滿足才繼續執行// 類似 Kotlin 的 require() 或 early return 模式guard let url = URL(string: url) else {completion(.failure(.invalidURL))return // 條件不滿足時提前退出}URLSession.shared.dataTask(with: url) { data, response, error inguard let data = data else {completion(.failure(.noData))return}do {let result = try JSONDecoder().decode(type, from: data)completion(.success(result))} catch {completion(.failure(.decodingError))}}.resume()}
}// 使用示例
struct User: Codable {let id: Intlet name: Stringlet email: String
}NetworkManager.shared.request(url: "https://api.example.com/users/1",type: User.self
) { result inswitch result {case .success(let user):print("用戶: \(user.name)")case .failure(let error):print("錯誤: \(error)")}
}
Kotlin 版本
import kotlinx.coroutines.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*sealed class NetworkResult<T> {data class Success<T>(val data: T) : NetworkResult<T>()data class Error<T>(val message: String) : NetworkResult<T>()
}class NetworkManager {companion object {val instance = NetworkManager()}private val json = Json { ignoreUnknownKeys = true }suspend inline fun <reified T> request(url: String): NetworkResult<T> {return withContext(Dispatchers.IO) {try {// 這里應該使用實際的 HTTP 客戶端,如 OkHttpval response = "" // 模擬響應val result = json.decodeFromString<T>(response)NetworkResult.Success(result)} catch (e: Exception) {NetworkResult.Error(e.message ?: "Unknown error")}}}
}// 使用示例
@Serializable
data class User(val id: Int,val name: String,val email: String
)// 在協程中使用
GlobalScope.launch {when (val result = NetworkManager.instance.request<User>("https://api.example.com/users/1")) {is NetworkResult.Success -> {println("用戶: ${result.data.name}")}is NetworkResult.Error -> {println("錯誤: ${result.message}")}}
}
Guard 語句詳解
什么是 Guard 語句
guard
是 Swift 中的一個關鍵字,用于提前退出模式(Early Exit Pattern)。它確保某個條件必須為真,否則就提前退出當前作用域。
基本語法
guard 條件 else {// 條件不滿足時執行的代碼return // 必須有退出語句
}
// 條件滿足時繼續執行的代碼
Guard vs If Let 對比
// 使用 if let(嵌套結構)
func processUser(name: String?) {if let userName = name {if userName.count > 0 {if userName != "admin" {print("處理用戶: \(userName)")// 更多邏輯...} else {print("管理員用戶")return}} else {print("用戶名為空")return}} else {print("用戶名為 nil")return}
}// 使用 guard(扁平結構)
func processUserWithGuard(name: String?) {guard let userName = name else {print("用戶名為 nil")return}guard userName.count > 0 else {print("用戶名為空")return}guard userName != "admin" else {print("管理員用戶")return}print("處理用戶: \(userName)")// 更多邏輯...
}
Guard 的優勢
- 減少嵌套:避免金字塔式的 if-else 嵌套
- 提高可讀性:主要邏輯更清晰
- 強制處理:必須在 else 塊中處理異常情況
- 作用域擴展:guard let 綁定的變量在后續代碼中可用
與 Kotlin 的對比
特性 | Swift Guard | Kotlin 等價寫法 |
---|---|---|
提前退出 | guard condition else { return } | if (!condition) return |
空值檢查 | guard let value = optional else { return } | val value = optional ?: return |
條件驗證 | guard user.isValid else { return } | require(user.isValid) { "Invalid user" } |
多條件檢查 | 多個 guard 語句 | takeIf { } 或多個 require |
Kotlin 對應寫法示例
// Swift guard 對應的 Kotlin 寫法
fun processUser(name: String?) {// Swift: guard let userName = name else { return }val userName = name ?: return// Swift: guard userName.isNotEmpty() else { return }if (userName.isEmpty()) return// Swift: guard userName != "admin" else { return }require(userName != "admin") { "Admin user not allowed" }println("處理用戶: $userName")
}// 或者使用 takeIf
fun processUserWithTakeIf(name: String?) {name?.takeIf { it.isNotEmpty() }?.takeIf { it != "admin" }?.let { userName ->println("處理用戶: $userName")}
}
核心要點
- 必須退出:guard 的 else 塊必須包含
return
、break
、continue
、throw
等退出語句 - 扁平化代碼:避免深層嵌套,讓主要邏輯更突出
- 變量作用域:guard let 綁定的變量在整個函數剩余部分可用
- 錯誤處理:適合用于參數驗證和前置條件檢查
- Kotlin 替代:主要使用
?:
操作符、require()
、takeIf()
等實現類似效果
總結
學習建議
- 從相似點開始:Swift 和 Kotlin 有很多相似的概念,可以快速上手
- 重點關注差異:特別注意可選類型、內存管理、語法細節的不同
- 多寫代碼:理論學習后要多實踐,加深理解
- 利用工具:使用 Xcode 的代碼補全和錯誤提示
- 閱讀官方文檔:Apple 的 Swift 文檔非常詳細
下一步學習方向
- iOS 框架學習:UIKit、SwiftUI、Core Data 等
- 架構模式:MVC、MVVM、VIPER 等
- 網絡編程:URLSession、Alamofire 等
- 數據持久化:Core Data、SQLite、UserDefaults
- 測試:XCTest、UI Testing
- 性能優化:Instruments、內存管理
常用資源
- Swift 官方文檔
- Apple Developer Documentation
- Swift Playgrounds
- Hacking with Swift
- Ray Wenderlich
本指南持續更新中,歡迎反饋和建議!