xDocxDoc
AI
前端
后端
iOS
Android
Flutter
AI
前端
后端
iOS
Android
Flutter
  • 如何避免写垃圾代码:iOS开发篇

如何避免写垃圾代码:iOS开发篇

前言:从Linus的愤怒说起

"这简直是垃圾!这种东西根本不该发给我,尤其是在合并窗口的后期。像这个毫无意义的make_u32_from_two_u16()'辅助函数',它让这个世界变得更糟糕居住。"

Linus Torvalds对Meta工程师代码的激烈批评,虽然语气强硬,却指出了一个关键问题:不必要的抽象会增加认知负荷。在iOS开发中,我们同样面临这样的挑战——如何在追求代码复用和保持代码清晰度之间找到平衡。

认知负荷理论在iOS开发中的应用

什么是认知负荷?

认知负荷指的是人类大脑在处理信息时所需的心理资源总量。在编程中,它体现在:

  1. 内在认知负荷:问题本身固有的复杂度
  2. 外在认知负荷:代码表达方式带来的额外负担
  3. 关联认知负荷:用于构建心理模式的资源
// 高认知负荷示例:不必要的抽象
protocol DataProcessor {
    func process(data: Data) -> ProcessedData
}

class ImageProcessor: DataProcessor {
    func process(data: Data) -> ProcessedData {
        // 复杂的处理逻辑
        guard let image = UIImage(data: data) else { 
            throw ProcessingError.invalidData 
        }
        // ...更多处理
        return processedImage
    }
}

// 使用时需要理解整个协议体系
let processor: DataProcessor = ImageProcessor()
let result = processor.process(data: imageData)
// 低认知负荷示例:直接明了的代码
func processImageData(_ data: Data) throws -> UIImage {
    guard let image = UIImage(data: data) else {
        throw ImageProcessingError.invalidData
    }
    
    // 清晰的图像处理逻辑
    let scaledImage = image.resize(to: CGSize(width: 300, height: 300))
    let filteredImage = scaledImage.applyFilter(.contrast(1.2))
    
    return filteredImage
}

// 使用时一目了然
let processedImage = try processImageData(imageData)

iOS开发中常见的"垃圾代码"模式

1. 过度工程化的协议抽象

// ❌ 不良实践:过度抽象
protocol NetworkRequestable {
    associatedtype Response: Decodable
    var endpoint: String { get }
    var method: HTTPMethod { get }
    var parameters: [String: Any]? { get }
}

protocol JSONParsable {
    associatedtype Model: Decodable
    func parse(_ data: Data) throws -> Model
}

protocol Cacheable {
    var cacheKey: String { get }
    var cacheExpiry: TimeInterval { get }
}

struct UserProfileRequest: NetworkRequestable, JSONParsable, Cacheable {
    typealias Response = UserProfile
    typealias Model = UserProfile
    
    let userId: String
    var endpoint: String { "/users/\(userId)" }
    var method: HTTPMethod { .get }
    var parameters: [String: Any]? { nil }
    var cacheKey: String { "user_profile_\(userId)" }
    var cacheExpiry: TimeInterval { 3600 }
    
    func parse(_ data: Data) throws -> UserProfile {
        return try JSONDecoder().decode(UserProfile.self, from: data)
    }
}

// ✅ 改进方案:适度的抽象
struct APIRequest {
    let endpoint: String
    let method: HTTPMethod
    let parameters: [String: Any]?
    let cacheKey: String?
    let cacheExpiry: TimeInterval?
}

func fetchUserProfile(userId: String) async throws -> UserProfile {
    let request = APIRequest(
        endpoint: "/users/\(userId)",
        method: .get,
        parameters: nil,
        cacheKey: "user_profile_\(userId)",
        cacheExpiry: 3600
    )
    
    let data = try await NetworkManager.shared.execute(request)
    return try JSONDecoder().decode(UserProfile.self, from: data)
}

2. 不必要的Helper函数泛滥

// ❌ 不良实践:无意义的helper函数
class UIHelper {
    static func makeLabel(text: String, 
                         fontSize: CGFloat, 
                         textColor: UIColor) -> UILabel {
        let label = UILabel()
        label.text = text
        label.font = UIFont.systemFont(ofSize: fontSize)
        label.textColor = textColor
        return label
    }
    
    static func makeButton(title: String, 
                          backgroundColor: UIColor) -> UIButton {
        let button = UIButton()
        button.setTitle(title, for: .normal)
        button.backgroundColor = backgroundColor
        return button
    }
}

// 使用这些"helper"反而增加了理解成本
let titleLabel = UIHelper.makeLabel(text: "欢迎", 
                                   fontSize: 16, 
                                   textColor: .black)
let actionButton = UIHelper.makeButton(title: "确定", 
                                      backgroundColor: .blue)

// ✅ 改进方案:直接创建或者使用合理的扩展
extension UILabel {
    convenience init(text: String, 
                    fontSize: CGFloat, 
                    color: UIColor = .black) {
        self.init()
        self.text = text
        self.font = UIFont.systemFont(ofSize: fontSize)
        self.textColor = color
    }
}

// 使用更清晰明了
let titleLabel = UILabel(text: "欢迎", fontSize: 16)
let actionButton = UIButton(type: .system).then {
    $0.setTitle("确定", for: .normal)
    $0.backgroundColor = .blue
}

3. 复杂的闭包和函数式编程滥用

// ❌ 不良实践:过度复杂的函数式链式调用
let processedItems = items
    .filter { $0.isActive }
    .map { item in
        return item.transformed { value in
            return value * coefficientCalculator(
                base: baseValue,
                modifier: environmentalModifier
            )
        }
    }
    .compactMap { $0.finalize() }
    .sorted { $0.priority > $1.priority }
    .flatMap { $0.components }

// ✅ 改进方案:分解为清晰的步骤
var activeItems = items.filter { $0.isActive }

var transformedItems: [ProcessedItem] = []
for item in activeItems {
    let coefficient = calculateCoefficient(
        base: baseValue,
        modifier: environmentalModifier
    )
    let transformed = transformItem(item, coefficient: coefficient)
    if let finalized = transformed.finalize() {
        transformedItems.append(finalized)
    }
}

let sortedItems = transformedItems.sorted { $0.priority > $1.priority }
let result = sortedItems.flatMap { $0.components }

iOS特定场景的认知负荷优化

1. UIKit vs SwiftUI的认知负荷考量

// UIKit示例:传统的MVC模式
class UserProfileViewController: UIViewController {
    var user: User?
    private let nameLabel = UILabel()
    private let emailLabel = UILabel()
    private let avatarImageView = UIImageView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        configureWithUser()
    }
    
    private func setupUI() {
        // 大量的布局代码...
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(nameLabel)
        NSLayoutConstraint.activate([
            nameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
            nameLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16)
        ])
        
        // 更多UI设置代码...
    }
    
    private func configureWithUser() {
        nameLabel.text = user?.name
        emailLabel.text = user?.email
        // 图片加载等...
    }
}

// SwiftUI示例:声明式UI降低认知负荷
struct UserProfileView: View {
    let user: User?
    
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            HStack {
                AsyncImage(url: user?.avatarURL) { image in
                    image.resizable()
                } placeholder: {
                    Color.gray
                }
                .frame(width: 60, height: 60)
                .clipShape(Circle())
                
                VStack(alignment: .leading) {
                    Text(user?.name ?? "")
                        .font(.headline)
                    Text(user?.email ?? "")
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
            }
            .padding()
        }
    }
}

2. 内存管理中的认知负荷陷阱

// ❌ 不良实践:复杂的内存管理
class DataManager {
    static let shared = DataManager()
    private var cache: [String: Any] = [:]
    private var observers: [NSObjectProtocol] = []
    
    func fetchData(for key: String, 
                  completion: @escaping (Result<Data, Error>) -> Void) {
        if let cached = cache[key] as? Data {
            completion(.success(cached))
            return
        }
        
        // 复杂的网络请求和缓存逻辑
        let request = URLRequest(url: URL(string: key)
        let task = URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
            guard let self = self else { return }
            
            if let error = error {
                completion(.failure(error))
                return
            }
            
            guard let data = data else {
                completion(.failure(NSError(domain: "NoData", code: -1)))
                return
            }
            
            self.cache[key] = data
            completion(.success(data))
            
            // 通知观察者
            self.notifyObservers(for: key, data: data)
        }
        task.resume()
    }
    
    private func notifyObservers(for key: String, data: Data) {
        // 复杂的观察者通知逻辑
    }
}

// ✅ 改进方案:使用现代并发框架简化内存管理
actor DataCache {
    private var storage: [String: Data] = [:]
    
    func data(for key: String) async throws -> Data {
        if let cached = storage[key] {
            return cached
        }
        
        let data = try await downloadData(from: key)
        storage[key] = data
        return data
    }
    
    private func downloadData(from key: String) async throws -> Data {
        let url = URL(string: key)!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
}

// 使用示例清晰简单
let data = try await DataCache().data(for: "https://example.com/data")

AI时代的iOS代码编写策略

1. 为AI助手优化的代码结构

// 🤖 AI友好的代码结构
struct UserProfileConfig {
    let userId: String
    let shouldLoadAvatar: Bool
    let cachePolicy: CachePolicy
    let timeout: TimeInterval
}

// 清晰的函数签名和职责分离
func loadUserProfile(config: UserProfileConfig) async throws -> UserProfile {
    // 1. 检查缓存
    if let cached = try await checkCache(for: config.userId, policy: config.cachePolicy) {
        return cached
    }
    
    // 2. 网络请求
    let userData = try await fetchUserData(
        userId: config.userId,
        timeout: config.timeout
    )
    
    // 3. 数据处理
    let profile = try processUserData(
        userData,
        loadAvatar: config.shouldLoadAvatar
    )
    
    // 4. 缓存结果
    try await cacheProfile(profile, for: config.userId)
    
    return profile
}

// 每个辅助函数都有明确的单一职责
private func checkCache(for userId: String, policy: CachePolicy) async throws -> UserProfile? {
    // 清晰的缓存检查逻辑
}

private func fetchUserData(userId: String, timeout: TimeInterval) async throws -> Data {
    // 清晰的网络请求逻辑
}

private func processUserData(_ data: Data, loadAvatar: Bool) throws -> UserProfile {
    // 清晰的数据处理逻辑
}

2. 测试中的认知负荷考虑

// ❌ 测试代码中的高认知负荷
func testUserProfileLoading() {
    let mockNetwork = MockNetworkService()
    let mockCache = MockCacheService()
    let mockParser = MockDataParser()
    let config = AppConfig.shared
    
    let manager = UserProfileManager(
        network: mockNetwork,
        cache: mockCache,
        parser: mockParser,
        config: config
    )
    
    mockNetwork.stubResponse = .success(testData)
    mockCache.stubResult = .empty
    mockParser.stubResult = testUser
    
    let expectation = self.expectation(description: "Profile loaded")
    
    manager.loadProfile(userId: "123") { result in
        switch result {
        case .success(let user):
            XCTAssertEqual(user.name, "Test User")
        case .failure:
            XCTFail("Should not fail")
        }
        expectation.fulfill()
    }
    
    waitForExpectations(timeout: 1)
}

// ✅ 低认知负荷的测试代码
func testUserProfileLoading() async throws {
    // 设置清晰的测试数据
    let testUser = User.testInstance()
    let testData = try JSONEncoder().encode(testUser)
    
    // 使用简单的测试依赖
    let service = UserProfileService(
        network: .mock(returning: testData),
        cache: .empty,
        parser: .standard
    )
    
    // 清晰的测试逻辑
    let result = try await service.loadProfile(userId: "123")
    
    // 明确的断言
    XCTAssertEqual(result, testUser)
}

// 测试辅助扩展
extension User {
    static func testInstance() -> User {
        User(
            id: "123",
            name: "Test User",
            email: "test@example.com"
        )
    }
}

extension NetworkService {
    static func mock(returning data: Data) -> Self {
        // 简单的mock实现
    }
}

实用工具和技巧

1. Xcode功能优化认知负荷

// 使用// MARK: 注释组织代码
class UserProfileViewController: UIViewController {
    
    // MARK: - Properties
    private var user: User?
    private var isLoading = false
    
    // MARK: - UI Components
    private let nameLabel = UILabel()
    private let avatarImageView = UIImageView()
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadData()
    }
    
    // MARK: - Setup
    private func setupUI() {
        configureLabel()
        configureImageView()
        setupConstraints()
    }
    
    // MARK: - Data Loading
    private func loadData() {
        guard !isLoading else { return }
        isLoading = true
        Task {
            await fetchUserProfile()
        }
    }
    
    // MARK: - Helper Methods
    private func configureLabel() {
        nameLabel.font = .preferredFont(forTextStyle: .headline)
        nameLabel.textColor = .label
    }
}

2. 代码审查清单

总结:编写高质量iOS代码的核心原则

在iOS开发中,始终将降低认知负荷作为首要目标。这意味着:

  1. 避免过早优化:不要为了抽象的"完美架构"而增加理解难度
  2. 保持代码局部性:相关代码应该放在一起,减少文件跳转
  3. 适度重复优于错误抽象:有时候重复的代码比错误的抽象更可取

🔧 工具使用

  1. 利用Xcode功能:合理使用// MARK:、代码折叠、快速帮助等功能
  2. 拥抱现代并发:使用async/await简化异步代码
  3. 编写AI友好代码:为代码助手提供清晰的上下文
最后更新: 2025/9/18 19:05