Implementing copy() in Swift

前端 未结 10 1459
难免孤独
难免孤独 2020-11-27 15:31

I want to be able to copy a custom class in Swift. So far, so good. In Objective-C I just had to implement the NSCopying protocol, which means implementing

10条回答
  •  Happy的楠姐
    2020-11-27 16:27

    I was looking for a simpler solution. The concept was simple enough, duplicate the data by initialization however in my case the object chain was large and nested.

    Luckily my objects were already conforming to Codable so I simply used Encode and Decode to deep-copy the entire object.

    Simple Example:

    class MyObject: Codable, CustomStringConvertible {
        var name: String
        var description: String { name }
        
        init(name: String) {
            self.name = name
        }
    }
    
    let originalArr = [MyObject(name: "a"), MyObject(name: "b")]
    
    do {
        let data = try JSONEncoder().encode(originalArr)
        let copyArr = try JSONDecoder().decode([MyObject].self, from: data)
        
        //modify
        copyArr.forEach { obj in
            obj.name = "\(obj.name) modified"
        }
    
        print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
    } catch {
        fatalError(error.localizedDescription)
    }
    

    Refactor (Advanced):

    To simplify future cases we can create a protocol and conform to it, allowing interesting ways to use it.

    protocol Copyable: Codable {
        func copy() -> Self
    }
    
    extension Copyable {
        func copy() -> Self {
            do {
                let encoded = try JSONEncoder().encode(self)
                let decoded = try JSONDecoder().decode(Self.self, from: encoded)
                return decoded
            } catch {
                fatalError(error.localizedDescription)
            }
        }
    }
    

    That's it. We can then conform our object to Copyable

    class MyObject: Copyable, CustomStringConvertible {
        var name: String
        var description: String { name }
        
        init(name: String) {
            self.name = name
        }
    }
    
    let a = MyObject(name: "A")
    let b = a.copy()
    b.name = "B"
    
    print(a.name, b.name) //-> "A B"
    

    We can quickly add support on Array as well:

    extension Array where Element: Copyable {
        func copy() -> [Element] {
            return self.map { $0.copy() }
        }
    }
    
    let originalArr = [MyObject(name: "a"), MyObject(name: "b")]
    let copyArr = originalArr.copy()
    copyArr.forEach { (obj) in
        obj.name = "\(obj.name) modified"
    }
    
    print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
    

提交回复
热议问题