Skip to content

Swift类复制详解:深拷贝与浅拷贝

基本概念

在Swift中,值类型(如结构体、枚举)和引用类型(类)有着根本不同的复制行为:

  • 值类型(如structenum):默认实现深拷贝,直接赋值会创建完整副本
  • 引用类型(如class):默认实现浅拷贝,只复制引用,而不是对象本身

类的实例复制主要分为以下两种方式:

  1. 浅拷贝:只复制对象的引用,所有副本共享同一份数据,任何副本的修改都会影响所有其他副本
  2. 深拷贝:创建一个完全独立的新对象,包括其所有属性和嵌套对象,修改副本不会影响原始对象
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. 数据隔离:防止副本对原对象数据的意外修改
  2. 并发安全:在多线程环境中避免共享可变状态
  3. 状态保存:保存对象的特定状态快照
  4. 编辑操作:实现"编辑副本而非原件"的功能

实现深拷贝的方法

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通用方案★★★★★★★★依赖第三方库
ObjectMapperJSON数据模型★★★★★★★★★主要针对JSON模型对象

性能测试数据 (相对时间,越小越好):

  • 手动copy: 1.0x
  • NSCopying: 1.2x
  • Codable: 5.8x
  • Runtime反射: 12.4x
  • 第三方库: 2.5-7.0x

最佳实践建议:

  1. 优先考虑手动实现copy()方法,特别是性能关键场景
  2. 复杂数据模型推荐使用Codable方案,结构清晰且维护简单
  3. 大型项目或团队协作场景可使用专业库,统一实现
  4. 根据对象的复杂度分层处理:
    • 简单对象:手动复制
    • 嵌套对象:递归复制或Codable
    • 循环引用:使用对象跟踪表
  5. 避免使用反射等动态特性,除非必要

总结

Swift中实现类深拷贝的方法多种多样,各有优缺点。合理选择取决于多方面因素:

  • 对象结构的复杂度(简单、嵌套、循环引用)
  • 性能要求(频率高低、实时性要求)
  • 项目架构(兼容性、集成度)
  • 团队熟悉程度(维护成本)
  • 未来扩展性(属性变更频率)

深拷贝是个持续挑战,特别是在处理复杂对象图和第三方对象时,需要权衡易用性、性能和可维护性。在实际项目中,往往需要结合多种方法来实现最佳效果。

参考资料: