如何避免写垃圾代码:iOS开发篇
前言:从Linus的愤怒说起
"这简直是垃圾!这种东西根本不该发给我,尤其是在合并窗口的后期。像这个毫无意义的
make_u32_from_two_u16()
'辅助函数',它让这个世界变得更糟糕居住。"
Linus Torvalds对Meta工程师代码的激烈批评,虽然语气强硬,却指出了一个关键问题:不必要的抽象会增加认知负荷。在iOS开发中,我们同样面临这样的挑战------如何在追求代码复用和保持代码清晰度之间找到平衡。
认知负荷理论在iOS开发中的应用
什么是认知负荷?
认知负荷指的是人类大脑在处理信息时所需的心理资源总量。在编程中,它体现在:
-
内在认知负荷:问题本身固有的复杂度
-
外在认知负荷:代码表达方式带来的额外负担
-
关联认知负荷:用于构建心理模式的资源
swift
// 高认知负荷示例:不必要的抽象
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)
swift
// 低认知负荷示例:直接明了的代码
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. 过度工程化的协议抽象
swift
// ❌ 不良实践:过度抽象
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函数泛滥
swift
// ❌ 不良实践:无意义的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. 复杂的闭包和函数式编程滥用
swift
// ❌ 不良实践:过度复杂的函数式链式调用
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的认知负荷考量
swift
// 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. 内存管理中的认知负荷陷阱
swift
// ❌ 不良实践:复杂的内存管理
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助手优化的代码结构
swift
// 🤖 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. 测试中的认知负荷考虑
swift
// ❌ 测试代码中的高认知负荷
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功能优化认知负荷
swift
// 使用// 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. 代码审查清单
graph TD
A[代码审查开始] --> B{是否有不必要的抽象?}
B -->|是| C[考虑内联或简化]
B -->|否| D{单个函数是否超过50行?}
C --> E[重构完成]
D -->|是| F[考虑分解函数]
D -->|否| G{命名是否清晰明确?}
F --> E
G -->|否| H[改进命名]
G -->|是| I{认知负荷是否最低?}
H --> E
I -->|否| J[优化代码结构]
I -->|是| K[批准代码]
J --> E
K --> E
总结:编写高质量iOS代码的核心原则
在iOS开发中,始终将降低认知负荷作为首要目标。这意味着:
-
避免过早优化:不要为了抽象的"完美架构"而增加理解难度
-
保持代码局部性:相关代码应该放在一起,减少文件跳转
-
适度重复优于错误抽象:有时候重复的代码比错误的抽象更可取
🔧 工具使用
-
利用Xcode功能 :合理使用
// MARK:
、代码折叠、快速帮助等功能 -
拥抱现代并发 :使用
async/await
简化异步代码 -
编写AI友好代码:为代码助手提供清晰的上下文