在Swift中实现copy()

时间:2014-06-16 11:26:47

标签: swift

我希望能够在Swift中复制自定义类。到现在为止还挺好。在Objective-C中,我只需要实现NSCopying协议,这意味着实现copyWithZone

例如,我有一个名为Value的基本类,它存储NSDecimalNumber

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

在Objective-C I中,可以轻松调用copy来复制我的对象。在Swift中,似乎无法调用copy。即使不需要区域,我是否真的需要拨打copyWithZone?我需要将哪个区域作为参数传递?

10 个答案:

答案 0 :(得分:27)

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中,它可能是创建复制构造函数(一种采用相同类型对象的初始化程序)的一种更常见的方法。

答案 1 :(得分:17)

您可以编写自己的复制方法

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 = Object()

//vs

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

答案 2 :(得分:17)

嗯,有一个非常简单的解决方案,您不必创建根类。

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 :(得分:3)

swift中的可复制实例

注意: 复制Class实例的这种方法的好处在于它不依赖于NSObject或objc代码,最重要的是它不会混淆“数据样式类”。相反,它扩展了扩展“数据样式类”的协议。通过这种方式,您可以将复制代码放在另一个地方而不是自己的数据中,从而更好地划分区域。只要在类之后对协议进行建模,类之间的继承也会得到处理。以下是此方法的示例:

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 Graphic Framework example

以上示例的代码如下:

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

答案 4 :(得分:3)

在我看来,更多的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)")

答案 5 :(得分:3)

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

答案 6 :(得分:2)

Swift中有两种主要的复杂数据类型 - 对象和结构 - 它们做了很多类似的事情,因为不确定它们的确切位置,你会被原谅。好吧,其中一个关键领域是复制:两个变量可以指向同一个对象,以便更改一个可以同时更改它们,而如果你尝试使用结构,你会发现Swift创建一个完整的副本,以便更改副本不影响原件。

有许多对象指向相同的数据可能很有用,但通常您需要修改副本,以便修改一个对象不会对其他任何内容产生影响。要完成这项工作,您需要做三件事:

让您的课程符合NSCopying。这不是严格要求,但它使您的意图清晰。 实现方法副本(带:),实际复制发生。 在您的对象上调用copy()。 这是一个完全符合NSCopying协议的Person类的例子:

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

答案 7 :(得分:1)

仅当您使用ObjectMapper库时: 喜欢这个

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

答案 8 :(得分:0)

Swift making copies of passed class instances

如果您在此处使用接受的答案中的代码(OP回答了他们自己的问题),只要您的类是NSObject的子类并在该帖子中使用复制协议,它将通过调用copyOfValues按预期工作( )功能。

有了这个,没有繁琐的设置或复制功能,你需要将所有实例变量分配给新实例。

我应该知道,我编写了那段代码并且只是测试了它XD

答案 9 :(得分:0)

我正在寻找一个更简单的解决方案。这个概念很简单,可以通过初始化来复制数据,但是在我的情况下,对象链很大且嵌套。

幸运的是,我的对象已经符合 Codable ,所以我只用了EncodeDecode来深复制整个对象。

简单示例:

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

重构(高级):

为简化将来的案例,我们可以创建一个协议并遵守该协议,允许使用有趣的方式使用它。

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

就是这样。然后我们可以使对象符合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"

我们也可以在Array上快速添加支持:

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]