Подтвердить что ты не робот

Реализация copy() в Swift

Я хочу, чтобы иметь возможность копировать пользовательский класс в Swift. Все идет нормально. В Objective-C мне просто пришлось реализовать протокол NSCopying, что означает реализацию copyWithZone.

В качестве примера у меня есть базовый класс с именем Value, в котором хранится NSDecimalNumber.

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

В Objective-C я мог просто просто вызвать copy для копирования моего объекта. В Swift, кажется, нет способа вызвать copy. Мне действительно нужно позвонить copyWithZone, даже если зона не нужна? И какую зону мне нужно передать в качестве параметра?

4b9b3361

Ответ 1

Метод copy определяется в NSObject. Если ваш пользовательский класс не наследуется от NSObject, copy не будет доступен.

Вы можете определить copy для любого объекта следующим образом:

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

Я предполагаю, что copy на самом деле не является чистым способом копирования объектов Swift. В Swift это, вероятно, более распространенный способ создания конструктора копирования (инициализатор, который принимает объект того же типа).

Ответ 2

Ну, для этого есть очень простое решение, и вам не нужно создавать класс root.

protocol Copyable {
    init(instance: Self)
}

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

Теперь, если вы хотите, чтобы ваш пользовательский класс мог копировать, вы должны согласовать его с протоколом Copyable и обеспечить реализацию init(instance: Self).

class A: Copyable {
    var field = 0

    init() {
    }

    required init(instance: A) {
        self.field = instance.field
    }

}

Наконец, вы можете использовать func copy() -> Self для любого экземпляра класса A для создания его копии.

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

Ответ 3

Вы можете просто написать свой собственный метод копирования

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

Преимущество этого в том, что вы используете подклассы вокруг своего проекта, вы можете вызвать команду "copy", и он скопирует подкласс. Если вы просто запустите новый для копирования, вам также придется переписать этот класс для каждого объекта...

var object:Object
....
//This code will only work for specific class
var objectCopy = new Object()

//vs

//This code will work regardless of whether you are using subClass or      superClass
var objectCopy = object.Copy

Ответ 4

Скопируемые экземпляры в swift

Примечание: Самое замечательное в этом подходе к копированию экземпляров класса заключается в том, что он не полагается на NSObject или objc-код, и, самое главное, он не загромождает "Data-Style-Class". Вместо этого он расширяет протокол, который расширяет "класс стиля данных". Таким образом, вы можете разделить себя лучше, имея код копирования в другом месте, чем данные. Наследование между классами также учитывается до тех пор, пока вы моделируете протоколы после классов. Вот пример такого подхода:

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

Примечание: Чтобы увидеть реализацию этого подхода в реальном коде: Затем проверьте эту графическую структуру для OSX: (PERMALINK) https://github.com/eonist/Element/wiki/Progress2#graphic-framework-for-osx

Различные формы используют один и тот же стиль, но каждый стиль использует вызов style.copy() для создания уникального экземпляра. Затем на этой копии устанавливается новый градиент, а не исходная ссылка:

Пример графической структуры StyleKit

Код для приведенного выше примера выглядит следующим образом:

/*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()

Примечание:
Вызов копии фактически выполняется в методе mix(). Это делается для того, чтобы код был более компактным, и экземпляр удобно возвращать сразу. PERMALINK для всех поддерживающих классов для этого примера: https://github.com/eonist/swift-utils

Ответ 5

По моему мнению, более Swifty - это использовать связанный тип в протоколе Copyable, который позволяет определить тип возвращаемого значения для копии метода. Другие способы не позволяют копировать дерево объектов следующим образом:

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

Ответ 6

Только если вы используете библиотеку ObjectMapper: сделайте это

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

Ответ 7

IMO, самый простой способ добиться этого:

protocol Copyable
{
  init(other: Self)
}

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

Реализован в структуре как:

struct Struct : Copyable
{
  var value: String

  init(value: String)
  {
    self.value = value
  }

  init(other: Struct)
  {
    value = other.value
  }
}

И в классе, как:

class Shape : Copyable
{
  var color: NSColor

  init(color: NSColor)
  {
    self.color = color
  }

  required init(other: Shape)
  {
    color = other.color
  }
}

И в подклассах такого базового класса, как:

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

Если вы хотите копировать массив типов для копирования:

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

Что тогда позволяет сделать простой код, например:

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

  let copies = shapes.copy()
}

Ответ 8

Быстрое создание экземпляров пройденного класса

Если вы используете код в принятом ответе (OP ответил на свой вопрос) здесь, пока ваш класс является подклассом NSObject и использует протокол копирования в этом сообщении, он будет работать, как ожидалось, вызывая copyOfValues ​​().

При этом нет утомительных функций настройки или копирования, где вам нужно назначить все переменные экземпляра для нового экземпляра.

Я должен знать, я написал этот код и просто протестировал его XD

Ответ 9

В Swift есть два основных сложных типа данных - объекты и структуры - и они делают так много вещей одинаково, что вас простят за то, что вы не точно знаете, где они отличаются. Ну, одна из ключевых областей сводится к копированию: две переменные могут указывать на один и тот же объект, так что изменение одного изменяет их оба, тогда как если бы вы попытались это сделать с помощью структур, вы обнаружите, что Swift создает полную копию, чтобы сменить копию не влияет на оригинал.

Наличие большого количества объектов, указывающих на одни и те же данные, может быть полезным, но часто вы хотите изменить копии, чтобы изменение одного объекта не влияло ни на что другое. Чтобы выполнить эту работу, вам нужно сделать три вещи:

Сделайте свой класс совместимым с NSCopying. Это строго не требуется, но это делает ваше намерение понятным. Внесите копию метода (с:), где происходит фактическое копирование. Вызовите копию() на свой объект. Вот пример класса Person, который полностью соответствует протоколу 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
    }
}

Обратите внимание, что копия (с:) реализуется путем создания нового объекта Person с использованием информации текущего человека.

Сделав это, вы можете проверить свое копирование следующим образом:

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