一、先明確:什麼是 iOS 中的 "垃圾代碼"?

在 iOS 開發中,垃圾代碼通常有這些特徵:

  • 代碼冗餘:重複的 UI 配置、網絡請求、數據解析邏輯散落在各處;
  • 耦合嚴重:ViewController 動輒上千行,既管 UI、又管業務、還管數據;
  • 命名混亂:變量名用temp/data/btn1,方法名無語義(如func doSomething());
  • 忽視邊界:無空值判斷、無異常捕獲,容易崩潰;
  • 性能低下:頻繁刷新 UI、不合理的緩存、主線程做耗時操作;
  • 可測性差:代碼緊耦合,無法單獨單元測試。

二、編碼規範:從基礎層面規避垃圾代碼

規範是避免垃圾代碼的第一道防線,重點關注命名、格式、語法三個核心:

1. 命名:見名知意,遵循 iOS 生態規範

  • 遵循 Apple 命名習慣
  • 類名:大駝峯(UserProfileViewController),避免拼音 / 縮寫(除非是通用縮寫如VC/VM);
  • 方法名:小駝峯 + 動詞開頭(func fetchUserInfo()/func updateAvatar(),而非func getUser()/func doAvatar());
  • 變量名:小駝峯 + 語義化(userNickname而非nameloginButton而非btn1);
  • 常量名:全大寫 + 下劃線分隔(static let MAX_LOGIN_RETRY_COUNT = 3)。
  • 避免歧義:比如data要明確是userListData/productDetailDataresult要明確是loginResult/requestResult

2. 格式:統一風格,提升可讀性

  • 用 Xcode 自帶的格式化工具(Ctrl+I),保證縮進、空格、換行統一;
  • 閉包 / 方法體超過 3 行時,拆分行書寫(比如 Then 庫的配置閉包,每行一個屬性);
  • 註釋只寫 "為什麼",不寫 "做什麼"(代碼本身能説明做什麼,比如:
    swift
// 錯誤:設置按鈕顏色
btn.backgroundColor = .red
// 正確:主按鈕使用品牌紅色,符合設計規範v2.1
btn.backgroundColor = .brandRed

3. 語法:用 Swift 現代語法替代冗餘寫法

  • 擴展(Extension) 拆分大類:將 ViewController 的代理、點擊事件、UI 配置拆到不同 Extension 中;
    swift
// 原垃圾代碼:所有邏輯堆在ViewController裏
// 優化:拆分成Extension
extension UserVC: UITableViewDelegate { ... } // 代理邏輯
extension UserVC { // UI配置邏輯
    func setupUI() { ... }
}
extension UserVC { // 網絡請求邏輯
    func fetchData() { ... }
}
  • 可選鏈 + 空合運算符 替代嵌套 if-let:
    swift
// 垃圾代碼
if let user = currentUser {
    if let avatar = user.avatar {
        imageView.image = avatar
    } else {
        imageView.image = defaultAvatar
    }
} else {
    imageView.image = defaultAvatar
}
// 優化
imageView.image = currentUser?.avatar ?? defaultAvatar
  • 枚舉替代魔法值:

    swift
// 垃圾代碼:魔法值散落在各處
if status == 1 { ... } else if status == 2 { ... }
// 優化:枚舉封裝
enum OrderStatus: Int {
    case pending = 1
    case paid = 2
    case shipped = 3
}
if let status = OrderStatus(rawValue: statusInt), status == .pending { ... }

三、架構設計:從結構上避免代碼混亂

垃圾代碼的核心根源是 "無架構",iOS 開發中最易落地的架構模式是MVVM(替代 MVC 的 "Massive View Controller"),輔以單一職責原則

1. 分層設計:每個組件只做一件事

組件

職責(只做這些事)

禁止做的事

View

純 UI 展示、響應點擊(UIView/ViewController)

處理業務邏輯、請求網絡

ViewModel

處理業務邏輯、轉換數據(View←→Model)

持有 View、直接操作 UI

Model

數據模型(結構體 / 類)、數據驗證

包含業務邏輯、網絡請求

Service

網絡請求、本地存儲(CoreData/Keychain)

處理 UI 邏輯、業務判斷

Router

頁面跳轉(可選)

處理業務邏輯、持有 View

示例:MVVM 拆分 ViewController 垃圾代碼

swift

// 垃圾代碼:VC既請求數據,又處理邏輯,又更新UI
class UserVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 網絡請求(不該在VC裏)
        URLSession.shared.dataTask(with: URL(string: "xxx")!) { data, _, _ in
            let user = try! JSONDecoder().decode(User.self, from: data!)
            // 業務邏輯(不該在VC裏)
            let showName = user.nickname ?? user.phone
            // 更新UI(該在VC裏,但邏輯太雜)
            DispatchQueue.main.async {
                self.nameLabel.text = showName
            }
        }.resume()
    }
}

// 優化:MVVM拆分
// 1. Model:純數據
struct User: Codable {
    let nickname: String?
    let phone: String
}

// 2. Service:網絡請求
class UserService {
    func fetchUser(completion: @escaping (User?) -> Void) {
        URLSession.shared.dataTask(with: URL(string: "xxx")!) { data, _, _ in
            guard let data = data else { completion(nil); return }
            let user = try? JSONDecoder().decode(User.self, from: data)
            completion(user)
        }.resume()
    }
}

// 3. ViewModel:業務邏輯
class UserVM {
    let service = UserService()
    var userName: String = ""
    
    func loadUser(completion: @escaping () -> Void) {
        service.fetchUser { [weak self] user in
            guard let self = self, let user = user else { 
                self?.userName = "未知用户"
                completion()
                return 
            }
            // 業務邏輯:處理顯示名稱
            self.userName = user.nickname ?? user.phone
            completion()
        }
    }
}

// 4. ViewController:只負責UI
class UserVC: UIViewController {
    let vm = UserVM()
    let nameLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadData()
    }
    
    func setupUI() { /* 只配置UI,無業務邏輯 */ }
    
    func loadData() {
        vm.loadUser { [weak self] in
            DispatchQueue.main.async {
                self?.nameLabel.text = self?.vm.userName
            }
        }
    }
}

2. 複用邏輯:封裝成工具類 / 擴展

  • 通用 UI 配置:封裝成UIView+Extension(比如按鈕圓角、文字樣式);
  • 網絡請求:封裝成NetworkManager單例(統一處理請求頭、超時、錯誤);
  • 數據解析:封裝成Model+Decode(統一處理 JSON 解析、默認值);
  • 常用工具:封裝成Utils(比如日期格式化、字符串加密)。

四、工程實踐:用流程和工具約束代碼質量

1. 強制代碼檢查

  • 集成SwiftLint:通過規則配置(.swiftlint.yml)強制遵守命名、格式、語法規範,比如禁止使用force unwrap!)、禁止空註釋;
  • 安裝:brew install swiftlint
  • 配置:在 Xcode Build Phases 中添加腳本,每次編譯自動檢查,違規則編譯失敗;
  • 集成Infer:靜態分析工具,檢測空指針、內存泄漏、資源未釋放等問題。

2. 單元測試:驗證代碼正確性

  • 對 ViewModel、Service、工具類編寫單元測試(XCTest),覆蓋核心業務邏輯;
  • 目標:核心代碼的測試覆蓋率≥80%,避免 "改一處崩全量";
  • 示例:測試 UserVM 的業務邏輯

    swift
import XCTest
@testable import YourApp

class UserVMTest: XCTestCase {
    func testUserName() {
        // 測試暱稱非空時,顯示暱稱
        let user = User(nickname: "張三", phone: "13800138000")
        let vm = UserVM()
        vm.user = user
        XCTAssertEqual(vm.userName, "張三")
        
        // 測試暱稱為空時,顯示手機號
        let user2 = User(nickname: nil, phone: "13800138000")
        vm.user = user2
        XCTAssertEqual(vm.userName, "13800138000")
    }
}

3. 避免常見坑點

  • 內存泄漏:用weak self避免閉包循環引用,用 Instruments 的 Leaks 工具檢測;
  • 主線程阻塞:所有耗時操作(網絡、數據庫、圖片解碼)放到子線程,用DispatchQueue.global().async
  • 過度封裝:不要為了封裝而封裝(比如一個簡單的按鈕點擊,沒必要拆成多個類);
  • 忽視版本兼容:用if #available(iOS 15.0, *)判斷系統版本,避免低版本崩潰;
  • 硬編碼:所有字符串(文案、接口地址)、數值(尺寸、超時時間)放到Constants常量類中。

五、團隊協作:統一標準,持續優化

  • 制定團隊編碼規範文檔:明確命名、格式、架構、註釋的統一標準;
  • 代碼 Review:提交代碼前必須經過 Review,重點檢查耦合、冗餘、邏輯漏洞;
  • 定期重構:每迭代 1-2 個版本,重構一次核心模塊的垃圾代碼(小步快跑,避免技術債堆積);
  • 學習 Apple 官方示例:參考 Apple 的 Sample Code(如 SwiftUI 示例、MVVM 示例),對齊官方最佳實踐。

總結

  1. 基礎層:用清晰的命名、統一的格式、現代的 Swift 語法,從書寫層面避免冗餘和混亂;
  2. 結構層:用 MVVM 分層設計,遵循單一職責原則,拆分 ViewController 的臃腫邏輯;
  3. 保障層:用 SwiftLint、單元測試、靜態分析工具,強制約束代碼質量;
  4. 流程層:通過團隊規範、代碼 Review、定期重構,持續優化代碼,避免技術債堆積。

核心原則:寫代碼時多想一步 "這段代碼別人能看懂嗎?改起來容易嗎?" —— 垃圾代碼往往是 "只圖自己寫得快,不管別人維護難" 的結果。