Implementing copy() in Swift

前端 未结 10 1398
难免孤独
难免孤独 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条回答
  • 2020-11-27 16:11

    The copy method is defined in NSObject. If your custom class does not inherit from NSObject, copy won't be available.

    You can define copy for any object in the following way:

    class MyRootClass {
        //create a copy if the object implements NSCopying, crash otherwise
    
        func copy() -> Any {
            guard let asCopying = ((self as AnyObject) as? NSCopying) else {
                fatalError("This class doesn't implement NSCopying")
            }
    
            return asCopying.copy(with: nil)
        }
    }
    
    class A : MyRootClass {
    
    }
    
    class B : MyRootClass, NSCopying {
    
        func copy(with zone: NSZone? = nil) -> Any {
            return B()
        }
    }
    
    
    var b = B()
    var a = A()
    
    b.copy()  //will create a copy
    a.copy()  //will fail
    

    I guess that copy isn't really a pure Swift way of copying objects. In Swift it is probably a more common way to create a copy constructor (an initializer that takes an object of the same type).

    0 讨论(0)
  • 2020-11-27 16:13

    IMO, the simplest way to achieve this is :

    protocol Copyable
    {
      init(other: Self)
    }
    
    extension Copyable
    {
      func copy() -> Self
      {
        return Self.init(other: self)
      }
    }
    

    Implemented in a struct as :

    struct Struct : Copyable
    {
      var value: String
    
      init(value: String)
      {
        self.value = value
      }
    
      init(other: Struct)
      {
        value = other.value
      }
    }
    

    And, in a class, as :

    class Shape : Copyable
    {
      var color: NSColor
    
      init(color: NSColor)
      {
        self.color = color
      }
    
      required init(other: Shape)
      {
        color = other.color
      }
    }
    

    And in subclasses of such a base class as :

    class Circle : Shape
    {
      var radius: Double = 0.0
    
      init(color: NSColor, radius: Double)
      {
        super.init(color: color)
    
        self.radius = radius
      }
    
      required init(other: Shape)
      {
        super.init(other: other)
    
        if let other = other as? Circle
        {
          radius = other.radius
        }
      }
    }
    
    
    class Square : Shape
    {
      var side: Double = 0.0
    
      init(color: NSColor, side: Double)
      {
        super.init(color: color)
    
        self.side = side
      }
    
      required init(other: Shape)
      {
        super.init(other: other)
    
        if let other = other as? Square
        {
          side = other.side
        }
      }
    }
    

    If you want to be able to copy an array of Copyable types :

    extension Array where Element : Copyable
    {
      func copy() -> Array<Element>
      {
        return self.map { $0.copy() }
      }
    }
    

    Which then allows you to do simple code like :

    {
      let shapes = [Circle(color: .red, radius: 5.0), Square(color: .blue, side: 5.0)]
    
      let copies = shapes.copy()
    }
    
    0 讨论(0)
  • 2020-11-27 16:13

    In my opinion, more Swifty way is to use associated type in Copyable protocol which allows define return type for method copy. Other ways don't allow to copy an object tree like this:

    protocol Copyable {
        associatedtype V
        func copy() -> V
        func setup(v: V) -> V
    } 
    
    class One: Copyable {
        typealias T = One
        var name: String?
    
        func copy() -> V {
            let instance = One()
            return setup(instance)
        }
    
        func setup(v: V) -> V {
            v.name = self.name
            return v
        }
    }
    
    class Two: One {
        var id: Int?
        override func copy() -> Two {
            let instance = Two()
            return setup(instance)
        }
    
        func setup(v: Two) -> Two {
            super.setup(v)
            v.id = self.id
            return v
        }
    }
    
    extension Array where Element: Copyable {
        func clone() -> [Element.V] {
            var copiedArray: [Element.V] = []
            for element in self {
                copiedArray.append(element.copy())
            }
            return copiedArray
        }
    }
    
    let array = [One(), Two()]
    let copied = array.clone()
    print("\(array)")
    print("\(copied)")
    
    0 讨论(0)
  • 2020-11-27 16:14

    Swift making copies of passed class instances

    If you use the code in the accepted answer(the OP answered their own question) here, so long as your class is a subclass of NSObject and uses the Copying protocol in that post it will work as expected by calling the copyOfValues() function.

    With this, no tedious setup or copy functions where you need to assign all the instance variables to the new instance.

    I should know, I wrote that code and just tested it XD

    0 讨论(0)
  • 2020-11-27 16:20

    You can just write your own copy method

    class MyRootClass {
        var someVariable:Int
        init() {
            someVariable = 2
        }
        init(otherObject:MyRootClass) {
            someVariable = otherObject.someVariable
        }
        func copy() -> MyRootClass {
           return MyRootClass(self)
        }
    }
    

    The benefit of this is when you are using subclasses around your project, you can call the 'copy' command and it will copy the subclass. If you just init a new one to copy, you will also have to rewrite that class for each object...

    var object:Object
    ....
    //This code will only work for specific class
    var objectCopy = Object()
    
    //vs
    
    //This code will work regardless of whether you are using subClass or      superClass
    var objectCopy = object.copy()
    
    0 讨论(0)
  • 2020-11-27 16:22

    There are two main complex data types in Swift – objects and structs – and they do so many things similarly that you'd be forgiven for not being sure exactly where they differ. Well, one of the key areas is down to copying: two variables can point at the same object so that changing one changes them both, whereas if you tried that with structs you'd find that Swift creates a full copy so that changing the copy does not affect the original.

    Having lots of objects point at the same data can be useful, but frequently you'll want to modify copies so that modifying one object doesn't have an effect on anything else. To make this work you need to do three things:

    Make your class conform to NSCopying. This isn't strictly required, but it makes your intent clear. Implement the method copy(with:), where the actual copying happens. Call copy() on your object. Here's an example of a Person class that conforms fully to the NSCopying protocol:

    class Person: NSObject, NSCopying {
        var firstName: String
        var lastName: String
        var age: Int
    
        init(firstName: String, lastName: String, age: Int) {
            self.firstName = firstName
            self.lastName = lastName
            self.age = age
        }
    
        func copy(with zone: NSZone? = nil) -> Any {
            let copy = Person(firstName: firstName, lastName: lastName, age: age)
            return copy
        }
    }
    

    Note that copy(with:) is implemented by creating a new Person object using the current person's information.

    With that done, you can test out your copying like this:

    let paul = Person(firstName: "Paul", lastName: "Hudson", age: 36)
    let sophie = paul.copy() as! Person
    
    sophie.firstName = "Sophie"
    sophie.age = 6
    
    print("\(paul.firstName) \(paul.lastName) is \(paul.age)")
    print("\(sophie.firstName) \(sophie.lastName) is \(sophie.age)")
    

    CheckThis

    0 讨论(0)
提交回复
热议问题