Swift类复制详解:深拷贝与浅拷贝
基本概念
在Swift中,值类型(如结构体、枚举)和引用类型(类)有着根本不同的复制行为:
- 值类型(如
struct
、enum
):默认实现深拷贝,直接赋值会创建完整副本 - 引用类型(如
class
):默认实现浅拷贝,只复制引用,而不是对象本身
类的实例复制主要分为以下两种方式:
- 浅拷贝:只复制对象的引用,所有副本共享同一份数据,任何副本的修改都会影响所有其他副本
- 深拷贝:创建一个完全独立的新对象,包括其所有属性和嵌套对象,修改副本不会影响原始对象
swift
// 对比值类型和引用类型的复制行为
struct UserStruct {
var username = "Anonymous"
}
class UserClass {
var username = "Anonymous"
}
// 结构体(值类型)的复制
var struct1 = UserStruct()
var struct2 = struct1 // 深拷贝
struct2.username = "Taylor"
print(struct1.username) // 输出 "Anonymous",不受影响
// 类(引用类型)的复制
let user1 = UserClass()
let user2 = user1 // 浅拷贝
user2.username = "Taylor"
print(user1.username) // 输出 "Taylor",因为两个变量引用同一对象
为什么需要深拷贝
在以下情况下,你可能需要类的深拷贝:
- 数据隔离:防止副本对原对象数据的意外修改
- 并发安全:在多线程环境中避免共享可变状态
- 状态保存:保存对象的特定状态快照
- 编辑操作:实现"编辑副本而非原件"的功能
实现深拷贝的方法
1. 手动实现copy方法(推荐简单场景)
最直接且高效的方式是为类实现自定义copy()
方法:
swift
class User {
var username = "Anonymous"
var age = 20
var preferences: [String: Bool] = [:]
func copy() -> User {
let user = User()
user.username = username
user.age = age
user.preferences = preferences // 注意:这是字典的浅拷贝
return user
}
func deepCopy() -> User {
let user = User()
user.username = username
user.age = age
user.preferences = preferences.mapValues { $0 } // 创建字典的深拷贝
return user
}
}
let user1 = User()
let user2 = user1.copy() // 深拷贝
user2.username = "Taylor"
print(user1.username) // 输出 "Anonymous",独立副本
2. 使用NSCopying协议(兼容Objective-C)
在需要与Objective-C交互或使用Cocoa框架时,遵循NSCopying
协议是标准做法:
swift
import Foundation
class User: NSObject, NSCopying {
var username = "Anonymous"
var friends = [User]()
var metadata: [String: Any] = [:]
func copy(with zone: NSZone? = nil) -> Any {
let user = User()
user.username = username
// 递归拷贝嵌套对象
user.friends = friends.map { $0.copy() as! User }
// 处理复杂类型
var copiedMetadata: [String: Any] = [:]
for (key, value) in metadata {
if let copyableValue = value as? NSCopying {
copiedMetadata[key] = copyableValue.copy()
} else {
copiedMetadata[key] = value
}
}
user.metadata = copiedMetadata
return user
}
}
// 使用方法
let originalUser = User()
let copiedUser = originalUser.copy() as! User
注意事项:
- 必须继承自
NSObject
或其子类 - 返回类型为
Any
,使用时需要类型转换 - 所有属性都需要手动复制,特别注意处理嵌套对象
3. 使用Codable协议(推荐复杂对象)
利用Swift的Codable
系统实现深拷贝,适用于大多数数据模型:
swift
class User: Codable {
var username: String
var age: Int
var friends: [User]? // 递归结构也能正确处理
// 需要特殊处理的非Codable属性
var settings: [String: Any] = [:]
enum CodingKeys: String, CodingKey {
case username, age, friends
// 注意:settings不包含在CodingKeys中
}
func deepCopy() -> User? {
do {
let encoder = JSONEncoder()
let data = try encoder.encode(self)
let copy = try JSONDecoder().decode(User.self, from: data)
// 手动复制非Codable属性
copy.settings = self.settings
return copy
} catch {
print("深拷贝失败: \(error)")
return nil
}
}
// 必须实现的初始化方法
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
username = try container.decode(String.self, forKey: .username)
age = try container.decode(Int.self, forKey: .age)
friends = try container.decodeIfPresent([User].self, forKey: .friends)
settings = [:] // 非Codable属性需要手动初始化
}
// 构造函数
init(username: String, age: Int) {
self.username = username
self.age = age
}
}
4. 使用Runtime反射(高级技巧)
使用第三方库Runtime
实现通用深拷贝解决方案,适用于复杂或动态变化的类:
swift
import Runtime
extension NSObject {
func deepCopy() throws -> Self {
// 获取类型信息
let info = try typeInfo(of: type(of: self))
// 创建实例
let copy = try createInstance(of: type(of: self)) as! Self
// 复制所有属性
for property in info.properties {
do {
// 获取属性值
let value = try property.get(from: self)
// 递归复制嵌套对象
if let objectValue = value as? NSObject {
try property.set(value: try objectValue.deepCopy(), on: copy)
} else {
// 值类型或非NSObject引用类型直接设置
try property.set(value: value, on: copy)
}
} catch {
print("复制属性 \(property.name) 失败: \(error)")
// 对特定属性的错误可以选择继续或抛出
continue
}
}
return copy
}
}
// 使用示例
class ComplexObject: NSObject {
var name: String = "测试"
var nestedObjects: [AnyObject] = []
var someComputedProperty: String { return "计算属性" }
}
do {
let original = ComplexObject()
let copy = try original.deepCopy()
// 使用复制的对象
} catch {
print("深拷贝失败: \(error)")
}
优点与风险:
- ✅ 无需手动编写每个属性的复制代码
- ✅ 自动处理所有属性,包括私有属性
- ⚠️ 性能开销较大,不适合频繁操作
- ⚠️ 依赖第三方库且可能失败
- ⚠️ Swift版本更新可能导致兼容性问题
专业第三方库推荐
1. DeepCopier(纯Swift实现)
这是一个简单易用的深拷贝库,专为Swift对象设计:
swift
import DeepCopier
class User {
var name: String
var friends: [User]
var settings: [String: Any]
init(name: String, friends: [User] = [], settings: [String: Any] = [:]) {
self.name = name
self.friends = friends
self.settings = settings
}
}
// 使用
let original = User(name: "Alice", friends: [User(name: "Bob")])
let copy = DeepCopier.deepCopy(of: original)
// 自定义复制行为
extension DeepCopier {
static func copy<T>(_ value: T) -> T where T: User {
var copy = User(name: value.name)
copy.friends = value.friends.map { DeepCopier.deepCopy(of: $0) }
copy.settings = value.settings // 默认浅拷贝
return copy as! T
}
}
2. ObjectMapper(适合JSON模型对象)
适合与网络API交互的模型对象,可以同时处理JSON序列化和对象复制:
swift
import ObjectMapper
class User: Mappable {
var name: String?
var friends: [User]?
var lastActive: Date?
required init?(map: Map) { }
init(name: String, friends: [User]? = nil) {
self.name = name
self.friends = friends
}
func mapping(map: Map) {
name <- map["name"]
friends <- map["friends"]
lastActive <- (map["last_active"], DateTransform())
}
// 深拷贝实现
func deepCopy() -> User? {
// 序列化为字典再重新映射
guard let dict = self.toJSON() else { return nil }
return User(JSON: dict)
}
}
// 使用示例
let user = User(name: "Zhang", friends: [User(name: "Li")])
let copy = user.deepCopy()
3. Swift Cloner (Prototype)
适用于Swift 5.1+的现代化深拷贝框架:
swift
import SwiftCloner
class MyClass: Clonable {
var id: Int = 1
var name: String = "测试"
var items: [String] = []
// 实现克隆协议
func clone() -> Self {
return Cloner.deepClone(self)
}
// 自定义克隆行为(可选)
static func customize<T>(_ builder: CloneBuilder<T>) {
// 跳过不需要复制的属性
builder.exclude(\.tempCache)
// 自定义某些属性的复制行为
builder.customize(\.complexProperty) { original in
return original.specialCopy()
}
}
}
// 使用
let original = MyClass()
let clone = original.clone()
特殊情况处理
循环引用处理
处理对象图中的循环引用是深拷贝的一大挑战:
swift
class Node {
var name: String
var parent: Node?
var children: [Node] = []
init(name: String, parent: Node? = nil) {
self.name = name
self.parent = parent
}
// 处理循环引用的深拷贝
func deepCopy() -> Node {
return self.deepCopy(parentMap: [:])
}
private func deepCopy(parentMap: [ObjectIdentifier: Node]) -> Node {
// 创建新节点
let copy = Node(name: self.name)
// 跟踪已复制的对象,防止循环引用导致无限递归
var updatedMap = parentMap
updatedMap[ObjectIdentifier(self)] = copy
// 复制子节点
copy.children = children.map { child in
if let existingCopy = parentMap[ObjectIdentifier(child)] {
return existingCopy
} else {
return child.deepCopy(parentMap: updatedMap)
}
}
// 处理父节点
if let parent = parent {
if let existingParentCopy = parentMap[ObjectIdentifier(parent)] {
copy.parent = existingParentCopy
} else {
copy.parent = parent.deepCopy(parentMap: updatedMap)
}
}
return copy
}
}
复制UIKit/AppKit对象
系统UI框架组件的复制需要特殊处理:
swift
extension UIView {
func deepCopy() -> UIView? {
// 很多UIKit对象不支持NSCoding完整序列化
// 对于简单视图,可以使用归档复制
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIView
} catch {
print("复制失败: \(error)")
return nil
}
// 复杂视图可能需要手动重建结构
// 此处仅为演示
}
}
性能对比与选择建议
方法 | 适用场景 | 性能 | 复杂度 | 可靠性 | 局限性 |
---|---|---|---|---|---|
手动copy | 简单类 | ★★★★★ | ★★ | ★★★★★ | 需维护,易遗漏 |
NSCopying | 需要OC兼容 | ★★★★ | ★★★ | ★★★★ | 需继承NSObject |
Codable | 复杂数据模型 | ★★ | ★★★★ | ★★★ | 非Codable类型需特殊处理 |
Runtime反射 | 动态类型/通用方案 | ★ | ★★★★★ | ★★ | 性能开销大,可能不稳定 |
DeepCopier | 通用方案 | ★★★ | ★★ | ★★★ | 依赖第三方库 |
ObjectMapper | JSON数据模型 | ★★★ | ★★★ | ★★★ | 主要针对JSON模型对象 |
性能测试数据 (相对时间,越小越好):
- 手动copy: 1.0x
- NSCopying: 1.2x
- Codable: 5.8x
- Runtime反射: 12.4x
- 第三方库: 2.5-7.0x
最佳实践建议:
- 优先考虑手动实现
copy()
方法,特别是性能关键场景 - 复杂数据模型推荐使用
Codable
方案,结构清晰且维护简单 - 大型项目或团队协作场景可使用专业库,统一实现
- 根据对象的复杂度分层处理:
- 简单对象:手动复制
- 嵌套对象:递归复制或Codable
- 循环引用:使用对象跟踪表
- 避免使用反射等动态特性,除非必要
总结
Swift中实现类深拷贝的方法多种多样,各有优缺点。合理选择取决于多方面因素:
- 对象结构的复杂度(简单、嵌套、循环引用)
- 性能要求(频率高低、实时性要求)
- 项目架构(兼容性、集成度)
- 团队熟悉程度(维护成本)
- 未来扩展性(属性变更频率)
深拷贝是个持续挑战,特别是在处理复杂对象图和第三方对象时,需要权衡易用性、性能和可维护性。在实际项目中,往往需要结合多种方法来实现最佳效果。
参考资料: