web-dev-qa-db-ja.com

Swiftで「ディープコピー」を行う方法

Objective-Cでは、次の方法でディープコピーできます。

 Foo *foo = [[Foo alloc] init];
 Foo *foo2 = foo.copy;

Swiftでこのディープコピーを行う方法は?

20
allenlinli

ディープコピー

あなたの例は、StackOverflowで説明されているように ディープコピー ではありません。オブジェクトの真のディープコピーを取得するには、多くの場合NSKeyedArchiverが必要です。

迅速なコピー

NSCopyingプロトコルは、すべてがポインターであり、任意のオブジェクトのコピーの生成を管理する方法が必要だったため、オブジェクトのコピーを提供するObjective-Cの方法です。 Swiftの任意のオブジェクトコピーの場合、MyObjectを別のMyObjectに初期化し、initで古いオブジェクトから新しいオブジェクトに値を割り当てる便利なイニシャライザーを提供できます。 -copyはObjective-Cで行いますが、Objective-Cは防御コピーを実行するため、通常は各サブオブジェクトでコピーを呼び出す必要があります。

let object = MyObject()
let object2 = MyObject(object)

ほとんどすべてが値渡しです。ほとんど。

ただし、Swift ほとんどすべてが値渡しです前述のリンクをクリックする必要があります)NSCopyingの必要性は大幅に減少します。プレイグラウンドでこれを試してください:

var array = [Int](count: 5, repeatedValue: 0)
print(unsafeAddressOf(array), terminator: "")
let newArray = array
print(unsafeAddressOf(newArray), terminator: "")
array[3] = 3
print(array)
print(newArray)

割り当てはポインターのコピーではなく、実際には新しい配列であることがわかります。構造体とクラスに関連するSwiftの非コピーバイセマンティックスを取り巻く問題の本当によく書かれた議論のために、私は Mike Ashの素晴らしいブログ を提案します。

最後に、Apple視聴できる WWDC 2015 Value Semanticsビデオ 。メモリがSwift内で処理される方法と、Objective-Cとの違い.

18

私の忘れられない未来へ:

Swift(親が子供を持つかもしれないし、それらの子供が子供を持つかもしれない/そうでないかもしれないなど)でツリースタイルオブジェクトのディープコピーを行う簡単な方法を探している他の人のために…

NSCoding(起動間の永続データ用)にクラスを設定している場合、次の操作を行うことにより、この機能を使用してこのツリー構造の特定のインスタンスのディープコピーを実行できました。

Swift 4

class Foo: NSObject, NSCoding {
   var title = ""
   var children: [Foo] = []

   *blah, blah, blah*

   // MARK: NSCoding
   override public func encode(with coder: NSCoder) {
      super.encode(with: coder)
      coder.encode(title as Any?, forKey: "title")
      coder.encode(children as Any?, forKey: "children")
   }

   required public init?(coder decoder: NSCoder) {
      super.init(coder: decoder)
      self.title = decoder.decodeObject(forKey: "title") as? String ?? ""
      self.children = decoder.decodeObject(forKey: "children") as? [Foo] ?? []
   }

}

その間…他の場所…

// rootFoo is some instance of a class Foo that has an array of child Foos that each can have their own same array

// Archive the given instance
let archive = NSKeyedArchiver.archivedData(withRootObject: rootFoo)

// Unarchive into a new instance
if let newFoo = NSKeyedUnarchiver.unarchiveObject(with: archive) as? Foo {

   // newFoo has identical copies of all the children, not references

}
7
Joniz

FooNSCopyingを実装するObjective-Cクラスである場合、以下が機能します。

var foo2 = foo.copy();

-copyはFoundationのプロパティ表記を使用して定義されていないため、Objective-Cでドット表記を使用できる場合でも、Swiftのプロパティとして扱うことはできません。ドット表記を使用しないでください(構文的には合法ですが)-copyは論理的にはオブジェクトのプロパティではなく、オブジェクトのコピーを作成するパラメーターを取らないメソッドです。

実装がFooインスタンスのすべてのメンバーもコピーしない限り、Objective-Cのディープコピーではないのと同様に、これはディープコピーではありません。

7
JeremyP