Implementando copy () no Swift

Eu quero ser capaz de copiar uma class personalizada no Swift. Por enquanto, tudo bem. Em Objective-C eu tive que implementar o protocolo NSCopying , o que significa implementar copyWithZone .

Como exemplo, tenho uma class básica chamada Value que armazena um NSDecimalNumber .

 func copyWithZone(zone: NSZone) -> AnyObject! { return Value(value: value.copy() as NSDecimalNumber) } 

Em Objective-C I, poderia facilmente chamar a copy para copiar meu object. No Swift, parece não haver maneira de chamar a copy . Eu realmente preciso chamar copyWithZone mesmo se nenhuma zona for necessária? E qual zona eu preciso passar como parâmetro?

O método de copy é definido no NSObject . Se a sua class personalizada não herdar do NSObject , a copy não estará disponível.

Você pode definir copy para qualquer object da seguinte maneira:

 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 

Eu acho que essa copy não é realmente uma maneira pura de copiar objects. No Swift, é provavelmente uma maneira mais comum de criar um construtor de cópia (um inicializador que usa um object do mesmo tipo).

Bem, existe uma solução realmente fácil para isso e você não precisa criar a class raiz.

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

Agora, se você quiser fazer com que sua class personalizada possa ser copiada, é necessário adaptá-la ao protocolo Copyable e fornecer a implementação de init(instance: Self) .

 class A: Copyable { var field = 0 init() { } required init(instance: A) { self.field = instance.field } } 

Finalmente, você pode usar func copy() -> Self em qualquer instância da class A para criar uma cópia dela.

 let a = A() a.field = 1 let b = a.copy() 

Você pode simplesmente escrever seu próprio método de cópia

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

O benefício disso é quando você está usando subclasss em torno do seu projeto, você pode chamar o comando ‘copy’ e ele copiará a subclass. Se você acabou de iniciar um novo para copiar, você também terá que rewrite essa class para cada 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() 

Instâncias copiáveis ​​no swift

NOTA: A melhor coisa sobre essa abordagem para copiar instâncias de class é que ela não depende de código NSObject ou objc e, o mais importante, não atrapalha a “class de estilo de dados”. Em vez disso, ele estende o protocolo que estende a “Classe Estilo de Dados”. Dessa forma, você pode compartimentar melhor com o código de cópia em outro local do que com os dados. A inheritance entre as classs também é cuidada, desde que você modele os protocolos após as classs. Aqui está um exemplo dessa abordagem:

 protocol IA{var text:String {get set}} class A:IA{ var text:String init(_ text:String){ self.text = text } } extension IA{ func copy() -> IA { return A(text) } } protocol IB:IA{var number:Int {get set}} class B:A,IB{ var number:Int init(_ text:String, _ number:Int){ self.number = number super.init(text) } } extension IB{ func copy() -> IB { return B(text,number) } } let original = B("hello",42) var uniqueCopy = original.copy() uniqueCopy.number = 15 Swift.print("uniqueCopy.number: " + "\(uniqueCopy.number)")//15 Swift.print("original.number: " + "\(original.number)")//42 

OBSERVAÇÃO: Para ver uma implementação dessa abordagem em código real: Em seguida, verifique esta Estrutura Gráfica para o OSX: (PERMALINK) https://github.com/eonist/Element/wiki/Progress2#graphic-framework-for-osx

As diferentes formas usam o mesmo estilo, mas cada estilo usa uma chamada style.copy () para criar uma instância exclusiva. Em seguida, um novo gradiente é definido nesta cópia, em vez de na referência original como esta:

Exemplo do StyleKit Graphic Framework

O código para o exemplo acima é assim:

 /*Gradients*/ let gradient = Gradient(Gradients.red(),[],GradientType.Linear,π/2) let lineGradient = Gradient(Gradients.teal(0.5),[],GradientType.Linear,π/2) /*Styles*/ let fill:GradientFillStyle = GradientFillStyle(gradient); let lineStyle = LineStyle(20,NSColorParser.nsColor(Colors.green()).alpha(0.5),CGLineCap.Round) let line = GradientLineStyle(lineGradient,lineStyle) /*Rect*/ let rect = RectGraphic(40,40,200,200,fill,line) addSubview(rect.graphic) rect.draw() /*Ellipse*/ let ellipse = EllipseGraphic(300,40,200,200,fill.mix(Gradients.teal()),line.mix(Gradients.blue(0.5))) addSubview(ellipse.graphic) ellipse.draw() /*RoundRect*/ let roundRect = RoundRectGraphic(40,300,200,200,Fillet(50),fill.mix(Gradients.orange()),line.mix(Gradients.yellow(0.5))) addSubview(roundRect.graphic) roundRect.draw() /*Line*/ let lineGraphic = LineGraphic(CGPoint(300,300),CGPoint(500,500),line.mix(Gradients.deepPurple())) addSubview(lineGraphic.graphic) lineGraphic.draw() 

NOTA:
A chamada de cópia é realmente feita no método mix (). Isso é feito para que o código possa ser mais compacto e uma instância seja convenientemente retornada imediatamente. PERMALINK para todas as classs de suporte para este exemplo: https://github.com/eonist/swift-utils

Na minha opinião, a maneira mais Swifty é usar o tipo associado no protocolo Copyable que permite definir o tipo de retorno para a cópia do método. Outras formas não permitem copiar uma tree de objects como esta:

 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)") 

IMO, a maneira mais simples de conseguir isso é:

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

Implementado em uma estrutura como:

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

E, em uma aula, como:

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

E em subclasss de uma class base como:

 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 } } } 

Se você quiser copiar uma matriz de tipos copiáveis:

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

Que então permite que você faça um código simples como:

 { let shapes = [Circle(color: .red, radius: 5.0), Square(color: .blue, side: 5.0)] let copies = shapes.copy() } 

Somente se você estiver usando a biblioteca do ObjectMapper: faça como esta

 let groupOriginal = Group(name:"Abc",type:"Public") let groupCopy = Mapper().mapAny(group.toJSON())! //where Group is Mapable 

Existem dois tipos de dados complexos principais em objects e estruturas do Swift – e eles fazem tantas coisas da mesma forma que você seria perdoado por não ter certeza exatamente onde eles diferem. Bem, uma das áreas-chave é copiar: duas variables ​​podem apontar para o mesmo object, de modo que mudar uma delas altera ambas, ao passo que se você tentasse isso com structs, descobriria que o Swift cria uma cópia completa para que a alteração da cópia não afeta o original.

Ter muitos objects apontando para os mesmos dados pode ser útil, mas freqüentemente você vai querer modificar as cópias para que a modificação de um object não tenha efeito em mais nada. Para fazer este trabalho, você precisa fazer três coisas:

Faça com que sua turma esteja em conformidade com o NSCopying. Isso não é estritamente necessário, mas deixa sua intenção clara. Implemente a cópia do método (com :), onde a cópia real acontece. Chame copy () no seu object. Aqui está um exemplo de uma class Person que está totalmente de acordo com o protocolo NSCopying:

 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 } } 

Observe que a cópia (com 🙂 é implementada criando um novo object Person usando as informações da pessoa atual.

Com isso feito, você pode testar sua cópia assim:

 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

Swift fazendo cópias de instâncias de class passadas

Se você usar o código na resposta aceita (o OP respondeu à sua própria pergunta) aqui, desde que sua class seja uma subclass de NSObject e use o protocolo Copying nesse post, ele funcionará como esperado chamando a function copyOfValues ​​().

Com isso, nenhuma configuração tediosa ou funções de cópia em que você precise atribuir todas as variables ​​de instância à nova instância.

Eu deveria saber, eu escrevi esse código e testei XD