7

So I know that swift array is implemented as struct, and it will do automatically copy by itself.

I have write my class MyClass and I have implemented copyWithZone to make copy of it

However, my swift array contains my MyClass objects, like:

var array = [MyClass]()

when I wan to make a copy of that array like

var anotherArray = array

It still does not call MyClassObject.copyWithZone, and later if I change the object property in array, anotherArray will also reflect the change.

How can I make a copy of that without writing a for loop to iterate every object?

It's not duplicated as deep copy for array of objects in swift because I cannot use struct to rewrite my class.

Community
  • 1
  • 1
Wingzero
  • 9,644
  • 10
  • 39
  • 80
  • No it's not, the answer is saying I have to use struct to automatically copy. However I cannot change it to struct. It's a big class. – Wingzero Dec 24 '15 at 02:40
  • Thanks for pointing that out, I'll cancel my close vote – Dan Beaulieu Dec 24 '15 at 02:41
  • You might want to consider adding an extension to Array. Read the question and the first answer on this page: http://stackoverflow.com/questions/24027116/how-can-i-extend-typed-arrays-in-swift However, you can't avoid using an explicit or an implicit loop. – LIProf Dec 24 '15 at 02:56
  • thanks! I will consider a extension – Wingzero Dec 24 '15 at 03:06

3 Answers3

9

As a simple statement, you could use code like this:

var copiedArray = array.map{$0.copy()}

Note that the term "deepCopy" is a little misleading for what you're talking about. What if the array is heterogeneous and contains other containers like arrays, dictionaries, sets, and other custom container and "leaf" objects? What you should really do is to create a protocol DeepCopiable and make it a requirement that any object that conforms to DeepCopiable require that any child objects also conform to the DeepCopiable protocol, and write the deepCopy() method to recursively call deepCopy() on all child objects. That way you wind up with a deep copy that works at any arbitrary depth.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • this is less words than a for loop, which is great; however, why swift array cannot directly to do so? It should know it contains class object and call copy()... – Wingzero Dec 24 '15 at 02:19
  • Because that's not how it works. Copy is a shallow copy. Usually that's what you want. Deep copying is MUCH more expensive, both in terms of memory and time. – Duncan C Dec 24 '15 at 02:42
  • So the conclusion is looping every object is the only solution? – Wingzero Dec 24 '15 at 02:42
  • 2
    A deep copy involves making a duplicate of every object in the array. On some level, you must iterate through the members and duplicate them. Even if Swift arrays had a deepCopy method, that method would internally need to iterate through the array's contents and duplicate them. The map statement gives you what you want without having to write a loop. – Duncan C Dec 24 '15 at 02:47
5

If Element of an array is of reference type, conform that Element with NSCopying and implement copyWithZone method.

class Book {
    var name: String

    init(_ name: String) {
        self.name  = name
    }
}

extension Book: NSCopying {
    func copy(with zone: NSZone? = nil) -> Any {
        return Book(name)
    }
}

Now implement an array extension to make a deep copy

extension Array where Element: NSCopying {
      func copy() -> [Element] {
            return self.map { $0.copy() as! Element }
      }
}

Test the code base

let b1 = Book("Book1")
let b2 = Book("Book2")

let list = [b1, b2]
let clonedList = list.copy()

clonedList is now copy of list and won't affect each-other if you make change to any of two.

ranjit.x.singh
  • 307
  • 4
  • 9
2

Thanks Duncan for your solution, here is an extension using it. Note that your class must adopt the NSCopying protocol. For how to do that see here https://www.hackingwithswift.com/example-code/system/how-to-copy-objects-in-swift-using-copy

extension Array {
    func copiedElements() -> Array<Element> {
        return self.map{
            let copiable = $0 as! NSCopying
            return copiable.copy() as! Element
        }
    }
}
Josh Bernfeld
  • 4,246
  • 2
  • 32
  • 35
  • Note that the term "deepCopy" is a little misleading. What if the array is heterogeneous and contains other containers like arrays, dictionaries, sets, and other custom container and "leaf" objects? What you should really do is to create a protocol `DeepCopiable` and make it a requirement that any object that conforms to `DeepCopiable` require that any child objects also conform to the `DeepCopiable` protocol, and write the `deepCopy()` method to recursively call `deepCopy()` on all child objects. That way you wind up with a deep copy that works at any arbitrary depth. – Duncan C Jan 08 '18 at 22:37
  • Great point. I'm going to rename the function from `deepCopy()` to `copiedElements()` to avoid confusion. – Josh Bernfeld Jan 08 '18 at 23:24