【问题标题】:How do you copy objects in Swift?如何在 Swift 中复制对象?
【发布时间】:2015-01-27 15:51:46
【问题描述】:

我有以下课程:

class Client {
  let name: String
  let age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
}

let wrongClient = Client(name: "John", age: 9)

我怎样才能创建一个新版本的wrongClient 与正确的年龄?

我想要以下内容:

let rightClient = Client(wrongClient, age: 42)

例如,OCaml 允许开发人员执行以下操作:

type client = {
  name : string;
  age : int;
}

let wrong_client = {name = "John"; age = 25}
let right_client = {wrong_client with age = 42}

或者在 Scala 中:

case class Client(name: String, age: Int)

val wrongClient = Client(name: "John", age: 9)
val rightClient = wrongClient.copy(age=42)

编辑

我想用 Swift 试验数据不变性和数据共享。

因为不可变数据意味着从其他值“生成”值,所以“复制”对象可能经常发生。所以我的问题真的是:如何使用 Swift 从其他对象轻松构造新对象?

编辑 2

我目前正在查看Swiftz' lenses

【问题讨论】:

  • 为什么要添加OCaml作为标签添加?
  • @JacksonTale 很抱歉,我只是想看看它是否修复了语法高亮。
  • @JacksonTale 感谢您的链接!虽然 Swift 并未被认为是一种函数式语言,但我发现拥有一些工具来构建不可变数据结构有点尴尬,但几乎没有一个工具可以轻松使用它们。
  • 我认为 Swift 就像 Java 8 或 Python 一样,是一种具有一些功能点的命令式语言?

标签: swift copy


【解决方案1】:

在我们的 Swift 代码生成器中,我们自动为每个类包含一个复制构造函数。生成器在这里:https://github.com/eclipse/agileuml

使用继承的构造函数示例如下:

class Person
{ init() { }

  init(copyFrom: Person) {
    self.name = copyFrom.name
    self.age = copyFrom.age
    self.parents = copyFrom.parents
  }

  func copy() -> Person
  { let res : Person = Person(copyFrom: self)
    return res
  }

  var name : String = ""
  var age : Int64 = 0
  var parents : [Person] = []
}

class Staff : Person
{ override init()
  { super.init() }

  init(copyFrom: Staff) {
    super.init(copyFrom: copyFrom)
    self.staffNo = copyFrom.staffNo
    self.boss = copyFrom.boss
  }

  override func copy() -> Staff
  { let res : Staff = Staff(copyFrom: self)
    return res
  }

  var staffNo : Int = 0
  var boss : [Staff] = []
}

序列、集合和映射也是逐个元素复制的。

【讨论】:

    【解决方案2】:

    您可以将 Client 实现为 struct 而不是 class,因为 struct 始终按值传递。

    struct Client {
        var name: String
        var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    
    var wrongClient = Client(name: "John", age: 18)
    var rightClient = wrongClient
    rightClient.age = 99
    

    将 wrongClient 分配给 rightClient 会创建一个副本。当你更新rightClient的age时,errorClient仍然是18。

    【讨论】:

      【解决方案3】:
      1. 使您的类符合 NSCopying。这不是严格要求的, 但这会让你的意图很明确。
      2. 实现方法copy(with:),实际复制发生的地方。
      3. 在您的对象上调用 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
        }
      

      注意,copy(with:) 是通过使用当前人的信息创建一个新的 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)")
      

      来源:https://www.hackingwithswift.com/example-code/system/how-to-copy-objects-in-swift-using-copy

      【讨论】:

        【解决方案4】:

        没有任何定义的简写。我会推荐类似的东西:

        class Client {
            let name: String
            let age: Int
        
            init(name: String, age: Int) {
                self.name = name
                self.age = age
            }
        
            private init(from: Client, age:Int) {
                self.name = from.name
                self.age = age
            }
        
            func withAge(age:Int) -> Client {
                return Client(from: self, age: age)
            }
        }
        
        let right_client = Client(name: "John", age: 9)
        let wrong_client = right_client.withAge(42)
        

        【讨论】:

        • 感谢您的回答,它对我有用
        【解决方案5】:

        您可以添加另一个 init 方法。

           required init(name: String, age: Int) {
               self.name = name
               self.age = age
           }
        
           convenience init(from: Client, withAge: Int) {
               self.init(name: from.name, age: withAge)
           }
        

        【讨论】:

        • 有没有办法为每个类自动执行此行为?
        • 我不知道。
        【解决方案6】:

        必须有更好的方法,但我目前的解决方案是这样的:

           init(name: String, age: Int) {
               self.name = name
               self.age = age
           }
        
           init(from: Client, name: Int? = nil, age: Int? = nil) {
               self.name = name ?? Client.name
               self.age = age ?? Client.age
           }
        

        【讨论】:

          【解决方案7】:

          如果您需要一个数组的副本,只需使用过滤器尝试它并使用始终正确的东西,例如:

          let oldTagValueKey = self.tagValueKey.filter({ $0.value.characters.isEmpty == false })
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-07-25
            • 1970-01-01
            • 2019-09-20
            • 2012-03-10
            • 1970-01-01
            • 2011-01-02
            • 2010-10-26
            • 1970-01-01
            相关资源
            最近更新 更多