
1. Swift 数组扩展实战指南作为iOS开发中最基础的数据结构Array在Swift中扮演着重要角色。但标准库提供的功能往往不能满足实际开发需求这时候扩展(Extension)就派上用场了。我在多个商业项目中积累了一套实用的Array扩展方案今天先分享其中最基础的几个增强功能。提示本文所有扩展都基于Swift 5.7语法建议在Xcode 14环境下测试1.1 为什么需要扩展Array标准库的Array虽然提供了map、filter等基础功能但在实际业务场景中经常遇到这些痛点安全访问元素时需要频繁写guard语句数组判空逻辑散落在各处需要手动处理数组越界问题缺少常用的数据转换方法比如从网络请求返回的JSON数组我们经常需要这样的操作guard let firstItem jsonArray.first else { return } guard jsonArray.count 5 else { return } let validItems jsonArray.filter { $0.isValid }这些样板代码会重复出现在各个模块通过合理的扩展可以大幅提升代码整洁度。2. 基础安全扩展实现2.1 安全下标访问最经典的扩展莫过于安全下标访问避免数组越界崩溃extension Array { subscript(safe index: Index) - Element? { return indices.contains(index) ? self[index] : nil } }使用示例let fruits [Apple, Orange, Banana] print(fruits[safe: 5] ?? Unknown) // 输出Unknown实现要点使用indices.contains先检查索引有效性返回Optional类型强制调用方处理nil情况保持与原生下标相似的调用语法2.2 非空判断扩展项目中最常见的判断就是数组是否非空extension Array { var isNotEmpty: Bool { !isEmpty } }虽然简单但能显著提升代码可读性// 之前 if !users.isEmpty { ... } // 之后 if users.isNotEmpty { ... }注意这种简单扩展适合团队代码规范统一的情况如果项目中原生isEmpty使用较多突然引入isNotEmpty可能造成风格混乱3. 数据转换扩展3.1 去重操作处理网络数据时经常需要去除重复元素extension Array where Element: Hashable { func unique() - [Element] { var seen SetElement() return filter { seen.insert($0).inserted } } }技术细节通过where子句限制Element必须遵守Hashable协议使用Set的insert返回值判断元素是否已存在时间复杂度O(n)空间复杂度O(n)使用示例let numbers [1, 2, 2, 3, 4, 4].unique() // [1, 2, 3, 4]3.2 分组转换后台返回的扁平数据经常需要按某个属性分组extension Array { func groupByKey: Hashable(_ keyForValue: (Element) - Key) - [Key: [Element]] { return Dictionary(grouping: self, by: keyForValue) } }实际应用struct Person { let name: String let age: Int } let people [ Person(name: Alice, age: 25), Person(name: Bob, age: 30), Person(name: Charlie, age: 25) ] let grouped people.groupBy { $0.age } /* [ 25: [Person(name: Alice, age: 25), Person(name: Charlie, age: 25)], 30: [Person(name: Bob, age: 30)] ] */4. 性能优化扩展4.1 批量更新操作当需要修改多个元素时传统方式会多次触发didSetextension Array { mutating func updateBatch(_ indices: [Int], transform: (inout Element) - Void) { indices.forEach { index in if indices.contains(index) { transform(self[index]) } } } }使用示例var numbers [1, 2, 3, 4, 5] numbers.updateBatch([0, 2, 4]) { $0 * 10 } // numbers变为 [10, 2, 30, 4, 50]优化点减少属性观察器的触发次数单次完成所有元素修改自动处理索引越界情况4.2 预取优化处理大型数组时prefetch可以提升性能extension Array { func prefetch(batchSize: Int, at index: Int, execute: ([Element]) - Void) { let start max(0, index - batchSize/2) let end min(count, index batchSize/2) execute(Array(self[start..end])) } }典型使用场景let largeArray (0..10000).map { $0 } largeArray.prefetch(batchSize: 50, at: 500) { batch in // 预加载处理 print(Prefetching \(batch.count) items) }5. 实战问题解决方案5.1 多条件排序产品列表经常需要多级排序extension Array { func sortedT: Comparable(by keyPaths: KeyPathElement, T...) - [Element] { return sorted { a, b in for keyPath in keyPaths { if a[keyPath: keyPath] ! b[keyPath: keyPath] { return a[keyPath: keyPath] b[keyPath: keyPath] } } return false } } }使用示例struct Product { let name: String let price: Double let sales: Int } let products [ Product(name: A, price: 100, sales: 50), Product(name: B, price: 80, sales: 100), Product(name: C, price: 80, sales: 80) ] // 先按价格升序再按销量降序 let sorted products.sorted(by: \.price, \.sales)5.2 分页加载处理列表分页是移动端常见需求extension Array { func paginated(page: Int, pageSize: Int) - (data: [Element], hasMore: Bool) { let start (page - 1) * pageSize guard start count else { return ([], false) } let end min(start pageSize, count) return (Array(self[start..end]), end count) } }实际调用let allItems (1...153).map { Item \($0) } let (page1, hasMore1) allItems.paginated(page: 1, pageSize: 50) let (page2, hasMore2) allItems.paginated(page: 4, pageSize: 50)6. 扩展的注意事项命名冲突风险 在大型项目中不同模块可能定义相同名称的扩展。建议添加模块名前缀module_safeSubscript使用更具体的名称safeIndexSubscript协议约束选择// 过于宽松 extension Array where Element: Any { ... } // 过于严格 extension Array where Element: CustomStringConvertible Equatable { ... } // 适度约束 extension Array where Element: Equatable { ... }性能考量避免在扩展中频繁创建临时数组链式调用时注意中间结果的产生大数据量操作考虑使用lazy测试覆盖要点func testSafeSubscript() { let array [1, 2, 3] XCTAssertNil(array[safe: -1]) XCTAssertNil(array[safe: 3]) XCTAssertEqual(array[safe: 0], 1) }我在实际项目中总结的经验是Array扩展应该保持高内聚性每个扩展只解决一个具体问题。下期我们会探讨更高级的Array扩展技巧包括与SwiftUI的深度结合属性包装器的应用并发安全访问方案