编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 质量保障

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

  • 技术模版

  • 技术规范

    • 技术规范
    • C++编程代码规范指南
    • Java编程代码规范指南
    • JavaScript编程规范指南
    • TypeScript编程规范指南
    • Python编程代码规范指南
    • Go编程代码规范指南
    • Kotlin编程代码规范指南
    • Swift编程代码规范指南
      • 目录
      • 01.规范概述
        • 1.1 为何需要代码规范
        • 1.2 核心目标
        • 1.3 要求等级
        • 1.4 Swift版本
      • 02.命名规范
        • 2.1 命名总表
        • 2.2 API设计核心原则
        • 2.3 正确与错误示例
        • 2.4 命名反模式
      • 03.代码格式规范
        • 3.1 缩进与空格 【必须】
        • 3.2 冒号与括号 【必须】
        • 3.3 换行与对齐
        • 3.4 空行与 import 顺序
      • 04.注释规范
        • 4.1 核心原则
        • 4.2 文档注释/Markup
        • 4.4 TODO 与 FIXME
      • 05.值类型与引用类型设计
        • 5.1 struct vs class 选型 【必须】
        • 5.2 枚举规范 【推荐】
        • 5.3 访问控制 【必须】
        • 5.4 初始化器规范 【推荐】
      • 06.函数与闭包规范
        • 6.1 函数设计原则
        • 6.2 闭包写法选择 【推荐】
        • 6.3 捕获列表与循环引用 【必须】
        • 6.4 自动闭包与逃逸闭包 【推荐】
      • 07.可选值处理
        • 7.1 guard let 提前返回 【推荐】
        • 7.2 可选链与 nil 合并 【必须】
        • 7.3 可选值变换
        • 7.4 强制解包边界 【必须】
        • 7.5 隐式解包可选值 【必须】
      • 08.错误处理
        • 8.1 throws vs Result 【推荐】
        • 8.2 do-catch 规范 【必须】
        • 8.3 try? 与 try! 的边界 【必须】
        • 8.4 自定义 Error 类型 【推荐】
      • 09.协议与扩展规范
        • 9.1 协议设计原则
        • 9.2 协议组合与关联类型 【推荐】
        • 9.3 扩展组织方式 【推荐】
        • 9.4 面向协议编程实践
      • 10.属性与属性包装器
        • 10.1 存储属性与计算属性 【推荐】
        • 10.2 SwiftUI 属性包装器 【必须】
        • 10.3 自定义属性包装器 【可选】
        • 10.4 lazy 与属性观察器 【推荐】
      • 11.并发规范
        • 11.1 async/await 结构化并发 【必须】
        • 11.2 Task 与 TaskGroup 【推荐】
        • 11.3 Actor 与数据隔离 【推荐】
        • 11.4 MainActor 标注 【必须】
        • 11.5 Sendable 与线程安全 【推荐】
      • 12.内存管理
        • 12.1 循环引用检测 【必须】
        • 12.2 weak 与 unowned 选择 【必须】
        • 12.3 闭包捕获列表 【必须】
      • 13.现代Swift特性
        • 13.1 不透明类型(some)【推荐】
        • 13.2 结果构建器(@resultBuilder)【可选】
        • 13.3 泛型与 where 子句 【推荐】
      • 14.工具链与自动化
        • 14.1 SwiftLint
        • 14.2 SwiftFormat
        • 14.3 CI 集成
        • 14.4 pre-commit 钩子 【推荐】
      • 15.常见反模式
      • 16.代码审查清单
      • 17.常见陷阱速查
        • 17.1 可选值与强制解包陷阱
        • 17.2 值类型陷阱
        • 17.3 并发陷阱
        • 17.4 内存陷阱
        • 17.5 协议与泛型陷阱
    • Rust编程代码规范指南
    • Shell编程代码规范指南
    • 项目代码提交规范
  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 测试经验

  • 技术
  • 技术规范
杨充
2021-02-10
目录

Swift编程代码规范指南

# Swift编程代码规范指南

本规范参考 Swift API Design Guidelines (opens new window) 及 SwiftLint Rules (opens new window),结合项目实践精简整理。

# 目录

  • 01.规范概述
    • 1.1 为何需要代码规范
    • 1.2 核心目标
    • 1.3 要求等级
    • 1.4 Swift版本
  • 02.命名规范
    • 2.1 命名总表
    • 2.2 API设计核心原则
    • 2.3 正确与错误示例
    • 2.4 命名反模式
  • 03.代码格式规范
    • 3.1 缩进与空格
    • 3.2 冒号与括号
    • 3.3 换行与对齐
    • 3.4 空行与import顺序
  • 04.注释规范
    • 4.1 核心原则
    • 4.2 文档注释Markup
    • 4.3 函数注释模板
    • 4.4 TODO与FIXME
  • 05.值类型与引用类型设计
    • 5.1 struct vs class选型
    • 5.2 枚举规范
    • 5.3 访问控制
    • 5.4 初始化器规范
  • 06.函数与闭包规范
    • 6.1 函数设计原则
    • 6.2 闭包写法选择
    • 6.3 捕获列表与循环引用
    • 6.4 自动闭包与逃逸闭包
  • 07.可选值处理
    • 7.1 guard let提前返回
    • 7.2 可选链与nil合并
    • 7.3 可选值变换
    • 7.4 强制解包边界
    • 7.5 隐式解包可选值
  • 08.错误处理
    • 8.1 throws vs Result
    • 8.2 do-catch规范
    • 8.3 try?与try!的边界
    • 8.4 自定义Error类型
  • 09.协议与扩展规范
    • 9.1 协议设计原则
    • 9.2 协议组合与关联类型
    • 9.3 扩展组织方式
    • 9.4 面向协议编程实践
  • 10.属性与属性包装器
    • 10.1 存储属性与计算属性
    • 10.2 SwiftUI属性包装器
    • 10.3 自定义属性包装器
    • 10.4 lazy与属性观察器
  • 11.并发规范
    • 11.1 async/await结构化并发
    • 11.2 Task与TaskGroup
    • 11.3 Actor与数据隔离
    • 11.4 MainActor标注
    • 11.5 Sendable与线程安全
  • 12.内存管理
    • 12.1 循环引用检测
    • 12.2 weak与unowned选择
    • 12.3 闭包捕获列表
  • 13.现代Swift特性
    • 13.1 不透明类型
    • 13.2 结果构建器
    • 13.3 泛型与where子句
  • 14.工具链与自动化
    • 14.1 SwiftLint
    • 14.2 SwiftFormat
    • 14.3 CI集成
    • 14.4 pre-commit钩子
  • 15.常见反模式
  • 16.代码审查清单
  • 17.常见陷阱速查
    • 17.1 可选值与强制解包陷阱
    • 17.2 值类型陷阱
    • 17.3 并发陷阱
    • 17.4 内存陷阱
    • 17.5 协议与泛型陷阱

# 01.规范概述

# 1.1 为何需要代码规范

疑惑:Swift 语法优雅,代码能编译就行,为什么还要花时间制定规范?

答疑:Swift 是一门"有态度的语言"——它的 API Design Guidelines 本身就是一种规范哲学。但同一个功能仍有多种实现路径:闭包可以尾随也可以不尾随,可选值可以 guard 也可以 if let,struct 和 class 的选型影响深远。规范的本质是用 Swift 社区认同的最佳实践写代码,让团队协作像 Swift 自身一样流畅。

# 1.2 核心目标

目标 说明
清晰度 代码读起来像英文句子,看一眼就知道在做什么
一致性 整个项目像一个人写的
安全性 从类型层面避免空指针崩溃、数据竞争、循环引用
可维护性 半年后自己还能快速读懂
可审查性 Code Review 聚焦逻辑而非格式

# 1.3 要求等级

  • 必须(Mandatory):必须采用,违反将在 Code Review 中被驳回
  • 推荐(Preferable):理应采用,特殊情况可不采用但需注释说明
  • 可选(Optional):团队自行决定

# 1.4 Swift版本

代码应针对 Swift 5.9+,优先使用稳定特性。实验性特性(如 Macros)需团队评审后引入。


# 02.命名规范

# 2.1 命名总表

类型 规则 示例
文件名 大驼峰(同主类型名) UserProfile.swift, String+Extensions.swift
类/结构体/枚举/协议 大驼峰 UserProfile, HTTPClient
枚举case 小驼峰 case success, case invalidEmail
方法/属性 小驼峰 fetchUser(), userName
布尔属性/方法 is/has/should 前缀 isEmpty, hasData, shouldRefresh
协议 名词描述能力 / able/ible/ing Codable, Equatable, Animating
常量 小驼峰 maxRetryCount, defaultTimeout
类型参数 大驼峰或以 T 结尾 Element, Value, RequestT
缩写 全大写或全小写 URL, JSON;userID, htmlString
工厂方法 参数为核心名词 UIView(frame:), Date(timeIntervalSinceNow:)

# 2.2 API设计核心原则

Swift API Design Guidelines 的三个核心原则:

  1. Usage at the call site(调用方清晰):命名从调用方的角度出发,读起来像英文句子
  2. Clarity over brevity(清晰胜于简洁):可以有省略,但语义必须明确
  3. Write documentation comments(写文档注释):对每个声明写摘要注释

# 2.3 正确与错误示例

// ✅ 方法读起来像英文句子
list.insert(element, at: index)
list.move(from: source, to: destination)

// ✅ 工厂方法:参数即是创建的核心
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let date = Date(timeIntervalSinceNow: 3600)

// ✅ 弱引用参数不带介词(其类型已说明角色)
func addObserver(_ observer: NSObject, forKeyPath path: String)

// ✅ 布尔属性读起来像断言
var isEmpty: Bool { count == 0 }
var isDescending: Bool
var hasChildren: Bool

// ❌ 错误示例
func doInsert(element: Element, at: Int)  // "do" 冗余
func makeView(frame: CGRect) -> UIView   // 应用 UIView(frame:) 工厂风格
var empty: Bool                          // 丢失 is 前缀,是"清空"还是"是否为空"?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.4 命名反模式

反模式 示例 改进
方法名冗余介词 func doFetch() func fetch()
首参数名与方法名重复 func addEmployee(employee:) func addEmployee(_:)
返回布尔无前缀 var valid: Bool var isValid: Bool
缩写含糊 usrMgr, cfg userManager, configuration
拼音命名 dianHua phoneNumber
get 多余 func getName() -> String var name: String

# 03.代码格式规范

# 3.1 缩进与空格 【必须】

// ✅ 缩进:4 个空格(不用 Tab)
class UserService {
    func process(id: String) {
        if isActive {
            updateStatus()
        }
    }
}

// ✅ 运算符两侧加空格
let result = a + b * c
let ok = (x > 0) && (y < 10)

// ✅ 逗号后加空格、冒号左边无空格、类型标注时右边有空格
func greet(name: String, age: Int) -> String
let dict: [String: Any] = ["key": 42]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.2 冒号与括号 【必须】

// ✅ 类型声明:冒号右边有空格
class User {
    let name: String
    let age: Int
}

// ✅ 字典与三元:冒号两边都有空格
let dict: [String: Int] = ["a": 1, "b": 2]
let value = condition ? "yes" : "no"

// ✅ 协议继承:冒号两边有空格
struct Dog: Animal, Codable { }

// ✅ 空集合用 () 和 [] 不用显式类型
let numbers = [Int]()          // ✅
let dict = [String: Any]()     // ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.3 换行与对齐

// ✅ 参数过多:左括号换行,参数对齐
func fetch(
    from url: URL,
    method: HTTPMethod = .get,
    headers: [String: String] = [:],
    timeout: TimeInterval = 30
) async throws -> Data

// ✅ 链式调用:用 . 开头换行
let activeNames = users
    .filter { $0.isActive }
    .sorted { $0.lastLogin > $1.lastLogin }
    .map { $0.name }
    .prefix(10)

// ✅ 条件过长时换行
if isUserLoggedIn
    && hasValidToken
    && !isTokenExpired
    && hasNetworkPermission {
    proceed()
}

// 单行长度建议不超过 120 字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 3.4 空行与 import 顺序

// ✅ import 按字母序,Apple 框架在前,第三方在后
import Foundation
import SwiftUI
import UIKit

import Alamofire
import Kingfisher

// ✅ 类型内不同逻辑组之间空一行
struct UserProfile {
    // MARK: - Properties
    let id: String
    var name: String
    var avatarURL: URL?

    // MARK: - Computed Properties
    var displayName: String {
        name.isEmpty ? "Unknown" : name
    }

    // MARK: - Initialization
    init(id: String, name: String) {
        self.id = id
        self.name = name
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 04.注释规范

# 4.1 核心原则

注释解释"为什么",代码说明"做了什么"。

// ❌ 差的注释:复述代码
count += 1  // count 加 1

// ✅ 好的注释:解释意图
count += 1  // 跳过 CSV 文件的标题行

// ✅ 记录决策原因
// 使用 O(n²) 双重循环而非哈希表,因为 n ≤ 20,哈希开销更大
func findDuplicates(in items: [Item]) -> [Item]

// ✅ 标注非显而易见的优化
// 用 lazy 过滤避免立即遍历全部数据(数据源可能 10w+)
let filtered = allItems.lazy.filter { $0.isActive }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.2 文档注释/Markup

/**
 用户管理服务,负责用户的 CRUD 和权限管理。

 线程安全性:使用 `actor` 保证内部状态线程安全。

 使用示例:
 ```swift
 let service = UserService(repo: repo, cache: cache)
 let user = await service.getUser(id: 123)
1
2
3
4
5
6
7
8
9
  • Note: 该类为单例,所有方法在 MainActor 上执行
  • Warning: deleteUser 是不可逆操作
  • Precondition: userId 必须 > 0 */ class UserService { // ... }

### 4.3 函数注释模板

```swift
/**
 根据 ID 查询用户信息。

 优先从缓存获取,未命中时查数据库并回填缓存。

 - Parameter userId: 用户唯一标识,必须 > 0
 - Returns: 用户对象;若不存在则返回 `nil`
 - Throws: `NetworkError.timeout` 当网络超时
 */
func getUser(userId: Int64) async throws -> User?
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.4 TODO 与 FIXME

// TODO(yc): 添加分页查询支持,预计 v2.0 实现
func getAllUsers() -> [User]

// FIXME(yc): userId 为 nil 时缺少降级策略,应展示空态页面
func loadUser(userId: String?)

// HACK(yc): 第三方 SDK 的 bug,等待官方修复后移除(#1234)
func workaroundForSdkBug()
1
2
3
4
5
6
7
8

# 05.值类型与引用类型设计

# 5.1 struct vs class 选型 【必须】

// ✅ 优先用 struct:值语义,线程安全,无继承需求
struct UserProfile {
    let id: String
    var name: String
    var email: String?
}

// ✅ 需要引用语义 / 共享可变状态的用 class
class DataManager {
    private var cache: [String: Any] = [:]

    func store(_ value: Any, for key: String) {
        cache[key] = value
    }

    func retrieve(_ key: String) -> Any? {
        cache[key]
    }
}

// ✅ 选型决策表
// struct → 数据模型、配置、API 响应、CGPoint/CGSize 等值类型
// class  → 控制器、管理器、ViewModel(引用同一对象)、需 deinit 的场景
// enum   → 有限集合:状态机、结果、HTTP 方法
// actor  → 需并发安全隔离的可变状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 5.2 枚举规范 【推荐】

// ✅ 枚举 + 关联值:表达状态和结果
enum LoadingState<T> {
    case idle
    case loading
    case loaded(T)
    case failed(Error)
}

// ✅ 带 RawValue 的枚举(Int/String)
enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case delete = "DELETE"
}

// ✅ 嵌套枚举限定作用域
struct API {
    enum Endpoint {
        case login
        case userProfile(id: String)
        case search(query: String, page: Int)
    }
}

// ❌ 不要为枚举的每个 case 都写 rawValue → 用关联值更强大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 5.3 访问控制 【必须】

// ✅ Swift 默认 internal(模块内可见),按需收紧
public class APIClient {
    public func fetchUsers() async throws -> [User]    // 公开 API
    func buildURL() -> URL                             // 模块内部使用(internal 默认)
    private func signRequest(_ req: URLRequest) -> URLRequest  // 仅内部使用
}

// ✅ 属性的 setter 比 getter 更严格
public private(set) var currentUser: User?  // 外部只读,内部可写

// ✅ 不导出内部类型 → internal 即可,没必要 public
// ❌ 不要把所有东西都标成 public(暴露内部实现)
1
2
3
4
5
6
7
8
9
10
11
12

# 5.4 初始化器规范 【推荐】

// ✅ struct:编译器自动合成 memberwise init,不需要自己写
struct Point {
    var x: Double
    var y: Double
}
let p = Point(x: 10, y: 20)   // 自动获得 Point(x:y:)

// ✅ 需要时提供便利 init
extension Point {
    init(origin: (Double, Double)) {
        self.init(x: origin.0, y: origin.1)
    }
}

// ✅ class:指定初始化器与便利初始化器
class UserViewController: UIViewController {
    let userId: String

    // 指定初始化器
    init(userId: String) {
        self.userId = userId
        super.init(nibName: nil, bundle: nil)
    }

    // 便利初始化器
    convenience init() {
        self.init(userId: UUID().uuidString)
    }

    required init?(coder: NSCoder) {
        fatalError("Use init(userId:)")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 06.函数与闭包规范

# 6.1 函数设计原则

// ✅ 按需省略第一个参数标签(当参数角色已由函数名表达时)
list.insert(element, at: index)              // element 省略标签
list.move(from: source, to: destination)     // from/to 保留标签

// ✅ 弱引用参数不带介词
func addObserver(_ observer: NSObject, forKeyPath path: String)

// ✅ 默认参数替代重载
func connect(host: String, port: Int = 8080, useTLS: Bool = true)

// ✅ 参数顺序:必选在前,可选在后
func search(query: String, page: Int = 1, pageSize: Int = 20)

// ❌ 不要用 Boolean 参数做行为切换 —> 拆成两个函数
// func save(data: Data, sync: Bool)   ❌
func saveLocal(_ data: Data)           // ✅
func saveRemote(_ data: Data) async    // ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 6.2 闭包写法选择 【推荐】

// ✅ 尾随闭包:最后一个参数是闭包且较长时
UIView.animate(withDuration: 0.3) {
    view.alpha = 0
}

// ✅ 多个尾随闭包(Swift 5.3+)
UIView.animate(withDuration: 0.3) {
    view.alpha = 0
} completion: { finished in
    view.removeFromSuperview()
}

// ✅ 简单闭包用 $0、$1(单表达式、上下文明确)
let names = users.map { $0.name }
let sorted = users.sorted { $0.score > $1.score }

// ✅ 复杂闭包显式命名参数(>5 行或多层嵌套时)
users.filter { user in
    let isValid = validate(user)
    let hasPermission = checkPermission(for: user)
    return isValid && hasPermission
}

// ✅ 闭包参数类型可推断时省略类型声明
let filtered = users.filter { $0.isActive }    // 省略 (User) -> Bool

// ❌ 不要过度使用 $0, $1, $2... → 超过 $1 显式命名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 6.3 捕获列表与循环引用 【必须】

// ✅ weak self:避免闭包与 self 互相强引用
class ViewController: UIViewController {
    var api: APIClient!

    func loadData() {
        api.fetch { [weak self] result in
            guard let self else { return }    // Swift 5.8+ 简写
            self.updateUI(with: result)
        }
    }
}

// ✅ unowned self:当 self 的生命周期保证长于闭包时
class AnimationHandler {
    func start() {
        DispatchQueue.main.async { [unowned self] in
            self.performStep()   // self 一定存在
        }
    }
}

// ✅ 捕获多个变量时:weak 和 strong 可混合
api.fetch { [weak self, unowned dependency] result in
    guard let self else { return }
    self.process(result, with: dependency)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 6.4 自动闭包与逃逸闭包 【推荐】

// ✅ @autoclosure:延迟求值,调用方不用写花括号
func assert(_ condition: @autoclosure () -> Bool, _ message: String) {
    if !condition() { fatalError(message) }
}
assert(x > 0, "x must be positive")   // 不需要 { x > 0 }

// ✅ @escaping:闭包在函数返回后才执行
func performLater(_ work: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        work()
    }
}

// ❌ 非逃逸闭包(默认行为)不要加 @escaping
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 07.可选值处理

# 7.1 guard let 提前返回 【推荐】

// ✅ guard let:提前返回,减少嵌套("快乐路径"在主逻辑中)
func process(user: User?) {
    guard let user else { return }
    guard let email = user.email else {
        showError("Email required")
        return
    }
    guard isValid(email) else {
        showError("Invalid email")
        return
    }
    // 主逻辑:所有前置条件已满足
    sendWelcomeEmail(to: email, name: user.name)
}

// ❌ if let 嵌套过深
func badProcess(user: User?) {
    if let user {
        if let email = user.email {
            if isValid(email) {
                sendWelcomeEmail(to: email, name: user.name)
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 7.2 可选链与 nil 合并 【必须】

// ✅ 可选链:多层嵌套属性的安全取值
let city = user?.address?.city?.name

// ✅ nil 合并运算符 ??
let displayName = user?.name ?? "Unknown"
let timeout = config?.timeout ?? 30

// ✅ ?? + 提前返回
func validate(user: User?) {
    let name = user?.name ?? ""  // 提供空字符串默认值
    guard !name.isEmpty else { return }
    proceed(with: name)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.3 可选值变换

// ✅ map:对可选值做变换
let uppercased = user?.name.map { $0.uppercased() }
let url = path.flatMap { URL(string: $0) }   // flatMap 展开双层 Optional

// ✅ 可选值比较
if let score, score > 60 { }   // Swift 5.7+ 简写(等价于 if let score = score)

// ✅ 可选值条件:逗号分隔多条件
if let name = user?.name, let email = user?.email, isValid(email) {
    sendEmail(to: email, name: name)
}

// ❌ 不要用 nil 做业务信号 → 用 Optional 或 Result
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.4 强制解包边界 【必须】

// ❌ 代码审查时,每个 ! 都必须有充分理由

// ✅ 唯一可接受的场景:
// 1. IBOutlet(Storyboard 保证非空)
@IBOutlet private var tableView: UITableView!

// 2. 测试代码中断言
XCTAssertNotNil(user)
XCTAssertEqual(user!.name, "Alice")

// 3. 程序逻辑已保证(极少数情况)
let url = URL(string: "https://api.example.com")!  // 硬编码 URL 必定有效
1
2
3
4
5
6
7
8
9
10
11
12

# 7.5 隐式解包可选值 【必须】

// ✅ IUO(Implicitly Unwrapped Optional)仅用于无法在 init 时赋值
//    的依赖(如 IBOutlet、注入)
class MyViewController: UIViewController {
    @IBOutlet private var nameLabel: UILabel!   // ✅ Storyboard 初始化

    var coordinator: AppCoordinator!             // ❌ 改成普通 Optional + guard
}

// ✅ 改进:用构造注入
class ModernViewController: UIViewController {
    private let viewModel: UserViewModel
    private let nameLabel: UILabel  // 代码创建的 UI

    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        self.nameLabel = UILabel()
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("Use init(viewModel:)")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 08.错误处理

# 8.1 throws vs Result 【推荐】

// ✅ throws:函数可能失败,调用方必须处理
func fetchUser(id: String) async throws -> User

// ✅ Result:错误需要跨异步边界传递 / 存储
func loadAll(ids: [String]) async -> [Result<User, Error>] {
    await withTaskGroup(of: Result<User, Error>.self) { group in
        for id in ids {
            group.addTask {
                do { return .success(try await fetchUser(id: id)) }
                catch { return .failure(error) }
            }
        }
        return await group.reduce(into: []) { $0.append($1) }
    }
}

// ✅ 选型建议
// throws → 同步 / 单步异步操作
// Result → 批量操作、需要存储错误结果、Combine 管道中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 8.2 do-catch 规范 【必须】

// ✅ 按错误类型分支捕获
func loadProfile() {
    do {
        let user = try fetchUser()
        updateUI(user)
    } catch NetworkError.timeout {
        showError("网络超时,请重试")
    } catch NetworkError.unauthorized {
        promptLogin()
    } catch {
        showError("未知错误: \(error.localizedDescription)")
        logError(error)
    }
}

// ✅ throws 函数内部 try:错误向上传播
func loadAndCache() async throws -> User {
    let user = try await api.fetchUser()       // 失败则向上 throw
    cache.store(user)
    return user
}

// ❌ 不要让 catch 块为空(吞掉错误)
// do { ... } catch { }  // ❌ 错误被沉默丢弃
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 8.3 try? 与 try! 的边界 【必须】

// ✅ try?:不关心具体错误,只需成功/失败
let config = try? loadConfig() ?? .default
let cached = try? cache.retrieve(key)

// ✅ try? + guard:失败即退出
guard let data = try? Data(contentsOf: fileURL) else {
    showError("无法读取文件")
    return
}

// ❌ try!:除非 100% 确定不会抛错(与 ! 同理)
// let data = try! Data(contentsOf: bundledURL)  // Bundle 中文件,可接受
1
2
3
4
5
6
7
8
9
10
11
12

# 8.4 自定义 Error 类型 【推荐】

// ✅ 用枚举实现 Error,清晰分类
enum NetworkError: LocalizedError {
    case timeout(after: TimeInterval)
    case unauthorized
    case serverError(code: Int, message: String)
    case noConnection

    var errorDescription: String? {
        switch self {
        case .timeout(let seconds):
            return "请求超时(\(seconds)秒)"
        case .unauthorized:
            return "未授权,请重新登录"
        case .serverError(let code, let message):
            return "服务器错误 \(code): \(message)"
        case .noConnection:
            return "网络连接不可用"
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 09.协议与扩展规范

# 9.1 协议设计原则

// ✅ 协议小而精:一个协议只做一件事
protocol Reusable {
    static var reuseIdentifier: String { get }
}

protocol Configurable {
    associatedtype Configuration
    func configure(with config: Configuration)
}

// ✅ 扩展提供默认实现(减少实现方的负担)
extension Reusable {
    static var reuseIdentifier: String {
        String(describing: self)
    }
}

// ✅ 协议组合:typealias 简化复杂类型
typealias CodableEquatable = Codable & Equatable
typealias ReusableCell = Reusable & Configurable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 9.2 协议组合与关联类型 【推荐】

// ✅ associatedtype:协议泛型
protocol Repository {
    associatedtype Item
    func fetch(id: String) async throws -> Item
    func save(_ item: Item) async throws
}

// ✅ 实现时指定具体类型
class UserRepository: Repository {
    typealias Item = User   // 也可省略,编译器自动推断

    func fetch(id: String) async throws -> User { ... }
    func save(_ user: User) async throws { ... }
}

// ✅ 泛型约束 + where
func sync<T: Repository>(_ repo: T) async throws where T.Item: Codable {
    let items = try await repo.fetch(id: "all")
    let data = try JSONEncoder().encode(items)   // Codable 保证可用
    // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 9.3 扩展组织方式 【推荐】

// ✅ 用 extension 分组:按协议实现、功能模块分组
class UserViewController: UIViewController {
    // 核心属性 + 生命周期
}

// MARK: - UITableViewDataSource
extension UserViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
}

// MARK: - UITableViewDelegate
extension UserViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}

// MARK: - Private Helpers
private extension UserViewController {
    func setupUI()
    func bindViewModel()
}

// ✅ 对系统类型的扩展放在独立文件(如 String+Extensions.swift)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 9.4 面向协议编程实践

// ✅ 协议 + 扩展 = 模板方法模式
protocol Validatable {
    func validate() -> Bool
}
extension Validatable {
    func validateAndExecute(_ action: () -> Void) {
        if validate() { action() }
    }
}

// ✅ SwiftUI 中广泛使用
protocol Shape {
    func path(in rect: CGRect) -> Path
}
extension Shape {
    func fill<S: ShapeStyle>(_ style: S) -> some View { ... }
}

// ❌ 不要用协议替代简单的组合:过度协议化反而增加复杂度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 10.属性与属性包装器

# 10.1 存储属性与计算属性 【推荐】

// ✅ let:不可变优先
struct UserProfile {
    let id: String          // 不可变
    var name: String        // 需要修改
    var email: String?      // 可选值
}

// ✅ 计算属性:无副作用,O(1)
var isEmpty: Bool { count == 0 }
var fullName: String { "\(firstName) \(lastName)" }

// ❌ 计算属性中不要有网络请求、数据库查询(应用方法)
// var remoteConfig: Config { ... }  // ❌ 副作用隐藏在 getter 中
1
2
3
4
5
6
7
8
9
10
11
12
13

# 10.2 SwiftUI 属性包装器 【必须】

// ✅ @State:View 内部状态(值类型)
struct ContentView: View {
    @State private var text: String = ""
    var body: some View {
        TextField("输入", text: $text)  // $ 获取 Binding
    }
}

// ✅ @StateObject:View 拥有并持有的 ObservableObject
struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
}

// ✅ @ObservedObject:外部传入的 ObservableObject
struct DetailView: View {
    @ObservedObject var viewModel: DetailViewModel
}

// ✅ @EnvironmentObject:环境注入的 ObservableObject
struct SettingsView: View {
    @EnvironmentObject var settings: AppSettings
}

// ✅ @Binding:双向绑定,不持有数据
struct ToggleView: View {
    @Binding var isOn: Bool
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 10.3 自定义属性包装器 【可选】

// ✅ 属性包装器:封装重复逻辑
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, _ range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

// 使用
@Clamped(0...100) var score = 50
score = 150  // 自动裁切为 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 10.4 lazy 与属性观察器 【推荐】

// ✅ lazy:首次访问时延迟初始化
private lazy var dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    return formatter
}()

// ✅ didSet:值变化后回调(常用于 UI 更新)
var isLoading: Bool = false {
    didSet {
        loadingIndicator.isHidden = !isLoading
    }
}

// ✅ willSet:值变化前回调(需新旧值对比时)
var currentPage: Int = 0 {
    willSet {
        print("即将从 \(currentPage) 切换到 \(newValue)")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 11.并发规范

# 11.1 async/await 结构化并发 【必须】

// ✅ 结构化并发:所有子任务都在作用域内
func loadAll() async throws -> (User, [Post], [Friend]) {
    async let user = fetchUser()
    async let posts = fetchPosts()
    async let friends = fetchFriends()
    return try await (user, posts, friends)  // 三个请求并发执行
}

// ✅ Task:手动启动顶层任务(通常只在 SwiftUI / UIKit 入口)
struct ContentView: View {
    @State private var user: User?
    var body: some View {
        List { ... }
            .task {                              // SwiftUI 的 task modifier
                user = try? await fetchUser()    // View 消失时自动取消
            }
    }
}

// ❌ 不要在 async 上下文中用 Task.detached ——破坏结构化并发
// ❌ 不要用 Task { } 在 ViewModel 中替代 .task()(需手动管理取消)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 11.2 Task 与 TaskGroup 【推荐】

// ✅ TaskGroup:动态数量的并发任务
func fetchAllUsers(ids: [String]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for id in ids {
            group.addTask { try await fetchUser(id: id) }
        }
        return try await group.reduce(into: []) { $0.append($1) }
    }
}

// ✅ 限制并发数(用 TaskGroup + 信号量 / AsyncChannel)
// ✅ Task 取消检查
func longRunningWork() async throws {
    for i in 0..<1000 {
        try Task.checkCancellation()   // 协作取消检查点
        await doStep(i)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 11.3 Actor 与数据隔离 【推荐】

// ✅ actor:保护可变状态,防止数据竞争
actor UserCache {
    private var storage: [String: User] = [:]

    func get(_ key: String) -> User? {
        storage[key]                     // actor 内部同步访问
    }

    func set(_ key: String, user: User) {
        storage[key] = user
    }
}

// 使用:所有访问都需要 await(编译器强制)
let cache = UserCache()
await cache.set("u1", user: User(...))
let cached = await cache.get("u1")

// ❌ 不要在 actor 内部长时间阻塞(actor 是可重入的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 11.4 MainActor 标注 【必须】

// ✅ 整个类标注 @MainActor(UI 相关代码)
@MainActor
final class UserViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isLoading = false

    func load() async {
        isLoading = true
        defer { isLoading = false }
        // MainActor 保证了 @Published 属性的更新在主线程
        users = (try? await api.fetchUsers()) ?? []
    }
}

// ✅ 特定方法标注 @MainActor
func updateCache(_ user: User) {
    cachedUser = user      // 后台线程可调
}

@MainActor
func updateUI(_ user: User) {
    nameLabel.text = user.name   // 必须在主线程
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 11.5 Sendable 与线程安全 【推荐】

// ✅ 值类型自动满足 Sendable(线程安全传递)
struct UserProfile: Sendable {   // Sendable 可省略,编译器自动推导
    let id: String
    var name: String
}

// ✅ 类手动声明 Sendable(必须满足条件)
final class ThreadSafeCounter: @unchecked Sendable {
    private let lock = NSLock()
    private var _count = 0

    var count: Int {
        lock.lock(); defer { lock.unlock() }
        return _count
    }
}

// ❌ 可变引用类型默认不满足 Sendable → 编译器警告
//    解决:改用 actor、final class + 锁、或 struct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 12.内存管理

# 12.1 循环引用检测 【必须】

// ❌ 经典循环引用
class Parent {
    var child: Child?
}
class Child {
    var parent: Parent?     // ← 强引用 Parent → 循环引用 → 泄漏
}

// ✅ 打破循环:一方用 weak
class Parent {
    var child: Child?
}
class Child {
    weak var parent: Parent?  // ✅ 弱引用
}

// ✅ 闭包中的 [weak self]
class ViewController {
    var api: API!
    func load() {
        api.fetch { [weak self] result in   // self → api → closure → self
            guard let self else { return }
            self.updateUI(result)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 12.2 weak 与 unowned 选择 【必须】

// ✅ weak:被引用对象可能为 nil(最常见)
class DetailView {
    weak var delegate: DetailViewDelegate?  // delegate 通常用 weak
}

// ✅ unowned:被引用对象生命周期 ≥ 当前对象
class Customer {
    let creditCard: CreditCard
}
class CreditCard {
    unowned let customer: Customer   // CreditCard 不会比 Customer 活得久
}

// ✅ 决策表
// weak    → 对方可能先释放(delegate、observers)
// unowned → 对方生命周期一定更长或相同(父子关系、同步闭包)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 12.3 闭包捕获列表 【必须】

// ✅ 多个捕获:混合 weak 和 unowned
api.fetch { [weak self, unowned cache] result in
    guard let self else { return }
    cache.store(result)
    self.updateUI(result)
}

// ✅ Swift 5.3+ 闭包中 self 可省略(当捕获 self 明确时)
class MyClass {
    var name: String
    func configure() {
        button.setAction { [weak self] in
            self?.name = "tapped"   // 显式 self,提醒可能已释放
        }
    }
}

// ❌ 不要忘了在长生命周期闭包(Timer、NotificationCenter)中加捕获列表
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
    self?.tick()  // ✅ 加 [weak self]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 13.现代Swift特性

# 13.1 不透明类型(some)【推荐】

// ✅ some View:隐藏具体类型,编译器保证类型一致
func makeLabel() -> some View {
    Text("Hello")
        .font(.title)
        .foregroundColor(.blue)
}

// ✅ some Protocol:返回实现了该协议的具体类型
func makeCollection() -> some Collection {
    [1, 2, 3]  // 返回 [Int],但对外隐藏
}

// ❌ 不要用 any View 替代 some View(any 有性能开销)
//   any View → 存在类型(existential),有运行时开销
//   some View → 不透明类型(opaque),编译期确定,零开销
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 13.2 结果构建器(@resultBuilder)【可选】

// ✅ SwiftUI 中的 DSL(声明式 UI 语法)
VStack {
    Text("Title")
    Text("Subtitle")
}

// ✅ 自定义 DSL(按需)
@resultBuilder
struct ArrayBuilder<T> {
    static func buildBlock(_ components: T...) -> [T] {
        components
    }
}

func build(@ArrayBuilder<String> _ builder: () -> [String]) -> [String] {
    builder()
}

let items = build {
    "A"
    "B"
    "C"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 13.3 泛型与 where 子句 【推荐】

// ✅ where 子句:为特定类型提供特殊实现
extension Array where Element == String {
    func joinedWithComma() -> String {
        joined(separator: ", ")
    }
}

// ✅ where 约束协议关联类型
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T where T: Hashable {
    let decoded = try JSONDecoder().decode(T.self, from: data)
    return decoded
}

// ✅ 泛型 + where 组合约束
extension Collection where Element: Numeric {
    func sum() -> Element {
        reduce(0, +)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 14.工具链与自动化

# 14.1 SwiftLint

# .swiftlint.yml
opt_in_rules:
  - force_unwrapping           # 检测 ! 强制解包
  - closure_spacing
  - empty_count
  - explicit_self
  - operator_usage_whitespace
  - prohibited_super_call
  - redundant_nil_coalescing
  - unowned_in_closure

disabled_rules:
  - trailing_whitespace
  - line_length

force_unwrapping:
  severity: error               # ! 强制解包直接报错

line_length: 120
type_body_length: 300
file_length: 500
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 14.2 SwiftFormat

// SwiftFormat:专注格式化(比 SwiftLint 更强大的格式化能力)
// 配置文件 .swiftformat
--indent 4
--maxwidth 120
--wraparguments before-first
--self insert                     // 强制插入 self
--importgrouping alphabetized
--stripunusedargs unnamed-only
1
2
3
4
5
6
7
8

# 14.3 CI 集成

# GitHub Actions 示例
- name: Swift Code Quality
  run: |
    swiftlint lint --strict       # 格式 & 风格检查(警告也视为错误)
    swiftformat --lint .          # 格式检查
    xcodebuild test \
      -scheme MyApp \
      -destination 'platform=iOS Simulator,name=iPhone 15'
1
2
3
4
5
6
7
8

# 14.4 pre-commit 钩子 【推荐】

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/nicklockwood/SwiftFormat
    rev: 0.53.0
    hooks:
      - id: swiftformat
1
2
3
4
5
6
# 安装
brew install pre-commit
pre-commit install             # 每次 commit 前自动运行
pre-commit run --all-files     # 手动全量检查
1
2
3
4

# 15.常见反模式

反模式 问题 改进
强制解包 ! 运行时崩溃 guard let / ?? / try?
try! 忽略错误 错误被掩盖,崩溃无上下文 try? 或 do-catch
闭包循环引用 内存泄漏 [weak self] + guard let self
巨型 ViewController 难以维护、无法测试 MVVM / MV / VIPER 拆分
var 过多 可变状态难跟踪 优先 let,确需可变用 @Published
Protocol 过于庞大 实现方负担重 拆分为多个小协议
NotificationCenter 未移除 observer 野指针崩溃 addObserver 的地方确保有对应 removeObserver
在 viewDidLoad 中做网络请求 阻塞 UI 用 async/await 或移到 viewWillAppear / .task

# 16.代码审查清单

每次 Code Review 时,按以下清单逐项检查:

## 命名
- [ ] 类型大驼峰,方法/属性小驼峰
- [ ] 布尔属性 is/has/should 前缀
- [ ] 方法读起来像英文句子(调用方视角清晰)
- [ ] 无拼音、单字母、含义模糊的命名

## 格式
- [ ] 缩进统一(4 空格)
- [ ] import 按字母序
- [ ] extension 合理分组(MARK 注释)
- [ ] 单行 ≤ 120 字符

## 安全
- [ ] 无强制解包(!),无 try!
- [ ] 可选值用 guard let / if let / ?? 处理
- [ ] 闭包中 [weak self] 防止循环引用
- [ ] NotificationCenter / Timer 确保移除 observer

## 值类型与引用
- [ ] 优先 struct(值语义)而非 class
- [ ] 枚举替代魔法数字/字符串
- [ ] 访问控制合理(不暴露内部实现)

## 并发
- [ ] UI 操作标记 @MainActor
- [ ] async/await 结构化并发,无 Task.detached 滥用
- [ ] 可变共享状态用 actor 保护
- [ ] Sendable 类型正确标注

## 内存
- [ ] 无循环引用(parent-child、闭包、delegate)
- [ ] weak 与 unowned 选择正确
- [ ] 闭包捕获列表完整

## 测试
- [ ] 核心逻辑有单元测试
- [ ] 正常路径和异常路径都覆盖
- [ ] async 代码用 XCTestExpectation / async test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 17.常见陷阱速查

以下陷阱即使在有经验的开发者中也常见,值得定期回顾。

# 17.1 可选值与强制解包陷阱

# 陷阱 描述 正解
1 ! 在 production 中 let name = user!.name 崩溃 guard let / ??
2 IBOutlet 用弱引用 @IBOutlet weak var label: UILabel? 每次访问解包 @IBOutlet private var label: UILabel!(强引用)
3 可选链 = nil 不报错 user?.address?.city 返回 nil 静默,丢失上下文 关键路径加 guard 提前失败
4 可选值比较 if optionalBool 在 Swift 中不允许 if optionalBool == true

# 17.2 值类型陷阱

# 陷阱 描述 正解
1 struct 中的 class 属性 struct 包含 class 属性 → 值语义被破坏 全用 struct 属性或明确文档
2 修改 struct 忘记 mutating 编译错误 加 mutating 关键字
3 Array 是值类型 var arr = [1,2]; let copy = arr; arr.append(3) — copy 仍是 [1,2] 理解 COW(写时复制)
4 enum 关联值不能直接比较 case loaded(User) 无 Equatable 时报错 让 User 实现 Equatable

# 17.3 并发陷阱

# 陷阱 描述 正解
1 Task {} 忘记取消 View 消失后 Task 仍在执行 用 .task { } modifier
2 在 MainActor 中做 IO 阻塞主线程 await withTaskGroup 在后台执行
3 actor 重入 await 后 actor 状态可能已变化 检查前置条件是否仍然成立
4 @Published 非主线程 后台线程修改 @Published 触发 UI 更新 → 紫色警告 标注 @MainActor / 用 await MainActor.run

# 17.4 内存陷阱

# 陷阱 描述 正解
1 delegate 用强引用 经典的循环引用场景 weak var delegate
2 Timer 强引用 target Timer.scheduledTimer 的 target 被持有 用 block-based API:scheduledTimer(withTimeInterval:repeats:block:)
3 DispatchQueue.main.asyncAfter 引用 self self 被 block 持有直到执行 [weak self]
4 lazy 闭包中的 self 编译器可能不警告但形成强引用 显式 [unowned self](单次执行的 lazy)

# 17.5 协议与泛型陷阱

# 陷阱 描述 正解
1 协议不能当类型用(有 Self/associatedtype) var delegate: any Equatable 不允许 用 any Equatable(Swift 5.7+)或类型擦除
2 扩展中的 @objc extension 中的方法加 @objc 不会生成 Objective-C 入口 在类声明中加,或用 @objc extension
3 where 子句顺序 extension Array where Element == Int 顺序重要 Element 在前,约束在后
4 关联类型歧义 编译器无法推断 associatedtype 显式 typealias

本文档将随 Swift 语言演进持续更新,欢迎通过 GitHub issues (opens new window) 反馈问题和建议。

#Swift#iOS#代码规范
上次更新: 2026/06/17, 11:39:29
Kotlin编程代码规范指南
Rust编程代码规范指南

← Kotlin编程代码规范指南 Rust编程代码规范指南→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式