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行目:
arr1
がarr2
に割り当てられています。 これにより、arr1
の詳細コピーが作成され、そのコピーがarr2
- 7行目から11行目に割り当てられます。
arr2
で行われた変更はarr1
に反映されません。
これがディープコピーです—完全に別々のインスタンスです。 同じ概念は、すべての値型で機能します。
いくつかのシナリオでは、値型にネストされた参照型が含まれている場合、ディープコピーは異なる種類の動作を明らかにします。 私たちは、今後のセクションでそれが表示されます。
浅いコピー—可能な限り複製しない
- 浅いコピーでは、ソースが指すオブジェクトも宛先が指すオブジェクトも指します。 そのため、メモリには1つのオブジェクトのみが作成されます。
- Collections—コレクションの浅いコピーは、コレクション構造のコピーであり、要素ではありません。 浅いコピーでは、2つのコレクションが個々の要素を共有するようになりました。
- 高速—参照のみがコピーされます。
- 参照型をコピーすると、浅いコピーが作成されます。上記のコードでは
,
- 1行目から8行目:
Address
クラスタイプ - 10行目:
a1
—Address
タイプ - 11行目:
a1
のインスタンスがa2
に割り当てられています。 これにより、a1
の浅いコピーが作成され、そのコピーがa2
に割り当てられ、参照のみがa2
にコピーされます。 - 16行目から19行目:
a2
で行われた変更は、確かにa1
に反映されます。

上の図では、a1
とa2
の両方が同じメモリアドレスを指していることがわかります。
参照型を深くコピーする
今のところ、参照型をコピーしようとするたびに、オブジェクトへの参照のみがコピーされることがわかっています。 新しいオブジェクトは作成されません。 完全に別のオブジェクトを作成したい場合はどうすればよいですか?
copy()
メソッドを使用して、参照型の詳細コピーを作成できます。 ドキュメントによると、
copy()—copy(with:)
によって返されたオブジェクトを返します。
これはNSCopying
プロトコルを採用するクラスのための便利なメソッドです。 copy(with:)
の実装がない場合は例外が発生します。
コードスニペット2で作成したAddress class
をNSCopying
プロトコルに準拠するように再構築しましょう。上記のコードの
,
- 1行目から14行目:
Address
クラス型はNSCopying
に準拠し、copy(with:)
メソッド - を実装しています16行目:
a1
—Address
型 - のインスタンス17行目:
a1
はa2
にcopy()
メソッドを使用して割り当てられます。 これにより、a1
の詳細コピーが作成され、そのコピーがa2
に割り当てられます。 - 22行目から25行目:
a2
で行われた変更はa1
に反映されません。

上の図から明らかなように、a1
とa2
の両方が異なるメモリ位置を指しています。
別の例を見てみましょう。 今回は、別の参照型を含む参照型であるネストされた参照型でどのように動作するかを見ていきます。上記のコードの
,
- 22行目:
p1
の詳細コピーをcopy()
メソッドを使用してp2
に割り当てます。 これは、いずれかの変更が他の変更に影響を与えてはならないことを意味します。 - 27行目から28行目:
p2's
name
およびcity
の値が変更されます。 これらはp1
に反映してはなりません。 - 30行目:
p1's
name
は期待どおりですが、その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
でアドレスをコピーして新しいインスタンスを作成しました。 これにより、アドレスオブジェクトへの参照のみがコピーされます。 これが、p1
とp2's
address
の両方が同じ場所を指している理由です。
したがって、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
オブジェクトはまだ浅いコピーされます。 あなたは下のメモリマップからそれを見ることができます。

arr1
とarr2
の両方の要素は、同じメモリ位置を指しています。 これは、参照型がデフォルトでシャローコピーされるのと同じ理由があるためです。
オブジェクトをシリアル化してからシリアル化解除すると、常に新しいオブジェクトが作成されます。 これは、値型と参照型の両方に有効です。
データをシリアル化およびシリアル化解除するために使用できるApiをいくつか紹介します:
- NSCoding-アーカイブと配布のためにオブジェクトを符号化および復号化できるようにするプロトコル。 それは
NSObject
から継承する必要があるので、それはclass
型オブジェクトでのみ動作します。 - 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行目に割り当てられています:
arr1
とarr2
は同じメモリアドレスを指しています - 7行目:
arr2
- 9行目と10行目:
arr1
とarr2
は異なるメモリ位置を指しています
ディープコピーとシャローコピーの詳細と、異なるデータ型を持つ異なるシナリオでどのように動作するかを知っています。 あなたは例の独自のセットでそれらを試してみて、あなたが得る結果を見ることができます。
続きを読む
私の他の記事を読むことを忘れないでください:
- Swift4のCodableに関するすべて
- iOSの通知について常に知りたかったすべて
- グラデーションで色付け—iOS
- Ios11のコーディング:コレクションに&ドロップウィジェット)ios10で
- Uicollectionviewcellの選択が簡単になりました。.!!