Payal Guptaによるどのように使うことができますか

オブジェクトのコピーは、常にコーディングのパラダイムに不可欠な部分でした。 Swift、Objective-C、JAVA、またはその他の言語であれば、さまざまなコンテキストで使用するためにオブジェクトをコピーする必要があります。

この記事では、Swiftでさまざまなデータ型をコピーする方法と、さまざまな状況でどのように動作するかについて詳しく説明します。

値型と参照型

Swiftのすべてのデータ型は、大きく2つのカテゴリ、つまり値型と参照型に分類されます。

  • 値型—各インスタンスはデータの一意のコピーを保持します。 このカテゴリに分類されるデータ型には、-all the basic data types, struct, enum, array, tuplesが含まれます。
  • 参照型—インスタンスはデータの単一のコピーを共有し、型は通常classとして定義されます。

両方のタイプの最も顕著な特徴は、コピー動作にあります。

深く浅いコピーとは何ですか?

インスタンスは、値型であろうと参照型であろうと、次のいずれかの方法でコピーできます。

ディープコピー—すべてを複製します

  • ディープコピーでは、ソースが指すオブジェクトがコピーされ、コピーが宛先が指すオブジェクトがコピーされます。 そのため、2つの完全に別々のオブジェクトが作成されます。
  • Collections—コレクションの詳細コピーは、元のコレクション内のすべての要素が複製された2つのコレクションです。
  • 競合状態になりにくく、マルチスレッド環境では良好に動作します。
  • 値型は深くコピーされます。上記のコードでは

,

  • 1行目:arr1—文字列の配列(値型)
  • 2行目:arr1arr2に割り当てられています。 これにより、arr1の詳細コピーが作成され、そのコピーがarr2
  • 7行目から11行目に割り当てられます。arr2で行われた変更はarr1に反映されません。

これがディープコピーです—完全に別々のインスタンスです。 同じ概念は、すべての値型で機能します。

いくつかのシナリオでは、値型にネストされた参照型が含まれている場合、ディープコピーは異なる種類の動作を明らかにします。 私たちは、今後のセクションでそれが表示されます。

浅いコピー—可能な限り複製しない

  • 浅いコピーでは、ソースが指すオブジェクトも宛先が指すオブジェクトも指します。 そのため、メモリには1つのオブジェクトのみが作成されます。
  • Collections—コレクションの浅いコピーは、コレクション構造のコピーであり、要素ではありません。 浅いコピーでは、2つのコレクションが個々の要素を共有するようになりました。
  • 高速—参照のみがコピーされます。
  • 参照型をコピーすると、浅いコピーが作成されます。上記のコードでは

,

  • 1行目から8行目:Addressクラスタイプ
  • 10行目:a1Addressタイプ
  • 11行目:a1のインスタンスがa2に割り当てられています。 これにより、a1の浅いコピーが作成され、そのコピーがa2に割り当てられ、参照のみがa2にコピーされます。
  • 16行目から19行目: a2で行われた変更は、確かにa1に反映されます。

上の図では、a1a2の両方が同じメモリアドレスを指していることがわかります。

参照型を深くコピーする

今のところ、参照型をコピーしようとするたびに、オブジェクトへの参照のみがコピーされることがわかっています。 新しいオブジェクトは作成されません。 完全に別のオブジェクトを作成したい場合はどうすればよいですか?

copy()メソッドを使用して、参照型の詳細コピーを作成できます。 ドキュメントによると、

copy()—copy(with:)によって返されたオブジェクトを返します。

これはNSCopyingプロトコルを採用するクラスのための便利なメソッドです。 copy(with:)の実装がない場合は例外が発生します。

コードスニペット2で作成したAddress classNSCopyingプロトコルに準拠するように再構築しましょう。上記のコードの

,

  • 1行目から14行目:Addressクラス型はNSCopyingに準拠し、copy(with:)メソッド
  • を実装しています16行目:a1Address
  • のインスタンス17行目: a1a2copy()メソッドを使用して割り当てられます。 これにより、a1の詳細コピーが作成され、そのコピーがa2に割り当てられます。
  • 22行目から25行目:a2で行われた変更はa1に反映されません。

上の図から明らかなように、a1a2の両方が異なるメモリ位置を指しています。

別の例を見てみましょう。 今回は、別の参照型を含む参照型であるネストされた参照型でどのように動作するかを見ていきます。上記のコードの

,

  • 22行目:p1の詳細コピーをcopy()メソッドを使用してp2に割り当てます。 これは、いずれかの変更が他の変更に影響を与えてはならないことを意味します。
  • 27行目から28行目:p2'snameおよびcityの値が変更されます。 これらはp1に反映してはなりません。
  • 30行目:p1'snameは期待どおりですが、そのcity? それは"Mumbai"すべきではありませんか? しかし、我々はそれが起こって見ることはできません。 "Bangalore"p2のためだけだったでしょうか? うん…その通り。?

ディープコピー…!? それはあなたから期待されていませんでした。 全部コピーすると言ったでしょ そして今、あなたはこのように行動しています。 なぜああなぜ。.?! 私は今何をしますか? ☠ ️

慌てないでください。 この中でメモリアドレスが何を言っているのか見てみましょう。

上の図から、我々はそれを見ることができます

  • p1 そしてp2は、予想どおりに異なるメモリ位置を指します。
  • しかし、それらのaddress変数はまだ同じ場所を指しています。 これは、それらを深くコピーした後でさえ、参照のみがコピーされることを意味します。

ご注意ください: 参照型をコピーするたびに、深くコピーする必要があることを明示的に指定するまで、デフォルトで浅いコピーが作成されます。

func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}

前にPersonクラスに実装した上記のメソッドでは、self.addressでアドレスをコピーして新しいインスタンスを作成しました。 これにより、アドレスオブジェクトへの参照のみがコピーされます。 これが、p1p2'saddressの両方が同じ場所を指している理由です。

したがって、copy()メソッドを使用してオブジェクトをコピーしても、オブジェクトの真の詳細コピーは作成されません。

参照オブジェクトを完全に複製するには: 参照型とすべてのネストされた参照型は、copy()メソッドを使用してコピーする必要があります。

let person = Person(self.name, self.address.copy() as? Address)

上記のコードをfunc copy(with zone: NSZone? = nil) -> Anyメソッドで使用すると、すべてが機能します。 あなたは下の図からそれを見ることができます。

真のディープコピー-参照型と値型

参照型のディープコピーを作成する方法をすでに見てきました。 もちろん、すべてのネストされた参照型でそれを行うことができます。

しかし、値型のネストされた参照型、つまりオブジェクトの配列、または構造体の参照型変数、またはタプルはどうですか? 私たちもcopy()を使ってそれを解決できますか? いいえ、私たちは、実際にはできません。 copy()メソッドはNSCopyingプロトコルを実装する必要があり、NSObjectサブクラスに対してのみ機能します。 値型は継承をサポートしていないため、copy()を使用することはできません。

2行目では、arr1の構造のみがディープコピーされますが、その中のAddressオブジェクトはまだ浅いコピーされます。 あなたは下のメモリマップからそれを見ることができます。

arr1arr2の両方の要素は、同じメモリ位置を指しています。 これは、参照型がデフォルトでシャローコピーされるのと同じ理由があるためです。

オブジェクトをシリアル化してからシリアル化解除すると、常に新しいオブジェクトが作成されます。 これは、値型と参照型の両方に有効です。

データをシリアル化およびシリアル化解除するために使用できるApiをいくつか紹介します:

  1. NSCoding-アーカイブと配布のためにオブジェクトを符号化および復号化できるようにするプロトコル。 それはNSObjectから継承する必要があるので、それはclass型オブジェクトでのみ動作します。
  2. Codable—JSONなどの外部表現との互換性のために、データ型をエンコード可能およびデコード可能にします。 これは、両方の値型—struct, array, tuple, basic data typesと参照型—classで機能します。

Addressクラスをもう少し再構築して、Codableプロトコルに準拠し、コードスニペット3で前に追加したすべてのNSCopyingコードを削除しましょう。

上記のコードでは、11-13行目はarr1の真のディープコピーを作成します。 以下は、メモリの場所の明確な画像を与える図です。

Copy on Write

Copy on writeは、値型をコピーするときのパフォーマンスを向上させる最適化手法です。

単一の文字列やInt、または他の値型をコピーするとしましょう。 しかし、何千もの要素の配列をコピーするときはどうですか? それはまだパフォーマンスの問題を作成しませんか? それをコピーして、そのコピーに変更を加えない場合はどうなりますか? その場合、私たちが使用した余分なメモリは無駄ではありませんか?

ここでは、書き込み中のコピーの概念が来ます—コピーするとき、各参照は同じメモリアドレスを指します。 Swiftが実際に元のインスタンスをコピーして変更を行うのは、参照の1つが基になるデータを変更したときだけです。

つまり、ディープコピーであろうとシャローコピーであろうと、オブジェクトのいずれかを変更するまで、新しいコピーは作成されません。上記のコードの

,

  • 2行目:arr1の詳細コピーがarr2
  • 4行目と5行目に割り当てられています: arr1arr2は同じメモリアドレスを指しています
  • 7行目:arr2
  • 9行目と10行目:arr1arr2は異なるメモリ位置を指しています

ディープコピーとシャローコピーの詳細と、異なるデータ型を持つ異なるシナリオでどのように動作するかを知っています。 あなたは例の独自のセットでそれらを試してみて、あなたが得る結果を見ることができます。

続きを読む

私の他の記事を読むことを忘れないでください:

  1. Swift4のCodableに関するすべて
  2. iOSの通知について常に知りたかったすべて
  3. グラデーションで色付け—iOS
  4. Ios11のコーディング:コレクションに&ドロップウィジェット)ios10で
  5. Uicollectionviewcellの選択が簡単になりました。.!!

コメントを残す

メールアドレスが公開されることはありません。