Swift初学者常见错误大全:从A到Z

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言

Swift 是 Apple 生态系统中强大且直观的编程语言。然而,其简洁的语法和严格的类型检查常常让初学者感到困惑。本文将分析 iOS 和 macOS 初学者最常犯的错误。我们将从字母 A(自动引用计数)到 Z(可选类型的显式解包)逐一讲解。每个要点都附有实际代码示例和如何避免问题的解释。

1. 可选类型使用错误(nil 与解包)

Swift 最强大的特性之一是对 nil 的安全处理。但初学者常常忽略可选类型,或在不安全的情况下使用强制解包(!)。

1.1. 未检查的强制解包

最常见的错误是在未确认值不为 nil 的情况下使用感叹号。

// ❌ 错误:如果 user 未找到,应用会崩溃
let user = users.first!
print(user.name)

// ✅ 正确:安全解包
if let user = users.first {
    print(user.name)
} else {
    print("用户未找到")
}

1.2. 忽略 guard let

许多初学者使用嵌套的 if let,造成“死亡金字塔”。请使用 guard let 实现函数的提前返回。

// ❌ 糟糕:嵌套的 if let
func process(user: User?) {
    if let unwrappedUser = user {
        if let email = unwrappedUser.email {
            print(email)
        }
    }
}

// ✅ 良好:guard let
func process(user: User?) {
    guard let unwrappedUser = user, let email = unwrappedUser.email else {
        return
    }
    print(email)
}

2. 内存管理(ARC 与强引用循环)

Swift 使用自动引用计数(ARC),但它并非魔法。初学者常因忘记闭包捕获而导致内存泄漏。

2.1. 闭包中的强引用

当传递一个捕获 self 的闭包,而 self 又持有该闭包时,就会产生循环引用。

class NetworkManager {
    var onComplete: (() -> Void)?
    
    func fetchData() {
        // ❌ 错误:对 self 的强引用
        onComplete = {
            self.updateUI()
        }
        
        // ✅ 正确:弱引用
        onComplete = { [weak self] in
            self?.updateUI()
        }
    }
    
    func updateUI() { /* ... */ }
}

2.2. 忘记使用 unowned

有时你确信对象不会为 nil。可以使用 [unowned self],但仅当你保证 self 不会在闭包之前被释放时。

// 危险:在异步调用中使用 unowned
class ViewController {
    func loadImage() {
        ImageService.shared.fetch { [unowned self] image in
            // 如果 self 已被释放,应用会崩溃
            self.imageView.image = image
        }
    }
}

// 更安全:使用 weak
func loadImage() {
    ImageService.shared.fetch { [weak self] image in
        self?.imageView.image = image
    }
}

3. 常见的语法陷阱

即使简单的语法,如果不了解 Swift 的细节,也可能令人困惑。

3.1. 混淆类与结构体

初学者常把所有东西都做成类,而忘记结构体是值类型,类是引用类型。

// ❌ 错误:在结构体内部修改属性
struct User {
    var name: String
    mutating func changeName(to newName: String) {
        name = newName // 没有 mutating 将无法编译
    }
}

// ✅ 对于类不需要 mutating
class UserClass {
    var name: String
    func changeName(to newName: String) {
        name = newName
    }
}

3.2. 错误使用 defer

defer

博客

书籍推荐