我希望能够在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
?我需要将哪个区域作为参数传递?
答案 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)
注意:强> 复制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()调用来创建唯一的实例。然后在此副本上设置新渐变,而不是像以下那样在原始参考上设置:
以上示例的代码如下:
/*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)")
答案 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
,所以我只用了Encode
和Decode
来深复制整个对象。
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]