あなたが言うように、#1は問題ではありません。あなたはSwiftにオブジェクトへのポインタを持っていません。あなたはその価値かそれへの参照のいずれかを持っています。あなたがその価値を持っているなら、それはコピーです。参照がある場合、保護されます。だからここに問題はない。
しかし、2番目と実験を考えてみて、驚いて驚いてはいけません。
var xs = [1,2,3,4]
for x in xs { // (1)
if x == 2 {
xs.removeAll() // (2)
}
print(x) // Prints "1\n2\n3\n\4\n"
}
xs // [] (3)
待ち、我々は(2)で値を吹き飛ばす際にどのようにすべての値を印刷しません。私たちは今非常に驚いています。
しかし、そうではありません。スウィフト配列は値です。 (1)のxs
は値です。それを変えることはできません。これは、「4要素を含む配列構造を含むメモリへのポインタ」ではありません。それは値[1,2,3,4]
です。 (2)では、「xs
が指し示すものからすべての要素を削除しません。「私たちはあなたのすべての要素を(つまり、すべてのケースで[]
だろう)削除した場合の結果の配列を作成し、xs
にその新しい配列を割り当て、がである事はxsを取る。悪い何も起こりません。
だから何「?すべてのインデックスを無効にし、」ドキュメントがでどういう意味それはまさにそれを意味し、我々はインデックスを生成した場合、彼らはもうダメじゃないんだ見てみましょう:。。
var xs = [1,2,3,4]
for i in xs.indices {
if i == 2 {
xs.removeAll()
}
print(xs[i]) // Prints "1\n2\n" and then CRASH!!!
}
xs.removeAll()
が呼び出されると、古い結果という約束がありませんxs.indices
はもう何も意味するものではありません。これらのインデックスは、収集したコレクションに対して安全に使用することはできません。
Swiftの "Invalidates indices"は、C++の "イテレータを無効にする"と同じではありません。コレクションインデックスを使用することは常に危険なので、コレクションのインデックス付けを避ける必要があります。それを助けることができます。代わりにそれらを繰り返します。何らかの理由でインデックスが必要な場合でも、インデックスを作成することなく、インデックスを取得するにはenumerate
を使用してください。
(サイドノート、dict["key"]
はdict
にインデックス付けされていません。その鍵は自分のインデックスではありませんので、辞書は少し混乱している。彼らのDictionaryIndex
インデックスで辞書にアクセスする彼らのInt
インデックスによって配列にアクセスするのと同じくらい危険です。)
上記はNSArray
には当てはまりません。反復中にNSArray
を変更すると、「反復中に反復されたコレクション」エラーが発生します。私はSwiftデータ型についてのみ議論しています。
EDIT:for-in
はそれがどのように動作するかにvery explicitある:
生成()メソッドは、その準拠タイプ、ジェネレータタイプでの値を得るために、収集式で呼び出されGeneratorTypeプロトコルに追加します。プログラムは、ストリーム上でnext()メソッドを呼び出すことによってループの実行を開始します。返された値がNoneでない場合、それは項目パターンに割り当てられ、プログラムはステートメントを実行してから、ループの最初で実行を続けます。そうでなければ、プログラムは代入を実行せず、ステートメントを実行しないため、for-inステートメントの実行が終了します。
返されたGenerator
は、struct
であり、コレクション値を含んでいます。その振る舞いを変更するために他の値を変更することは期待できません。覚えておいてください。[1,2,3]
は4
と変わりません。それらは両方とも価値です。それらを割り当てると、コピーが作成されます。そのため、コレクション値を使ってジェネレータを作成すると、ジェネレータを4番に作成した場合と同じように、その値をスナップショットすることになります(これは、ジェネレータが本当に値ではないので、興味深い問題を引き起こします。 Swift stdlibはこれを修正しています。例えば、AnyGenerator
を参照してください。しかし、配列の値はまだ残っていますが、他の配列の値を変更することは決してありません。
"Structures and Enumerations Are Value Types"も参照してください。これは、Swiftの値タイプの重要性について詳しく説明しています。配列は単なる構造体です。
はい、論理的にコピーしています。 Swiftには、不要なときに実際のコピーを最小限に抑えるための最適化が多数用意されています。あなたのケースでは、辞書が反復されている間にその辞書を突然変異させると、それは強制的にコピーを起こします。特定の値のバッキングストレージの唯一の消費者であれば、変異は安いです。しかし、そうでない場合はO(n)です。長いスモール・コレクション:スウィフト・コレクションはコピー・オン・ライトであり、単純に配列を渡しても実際のメモリーは割り振られたりコピーされたりしません。これはスウィフトの組み込みによって決定されます(isUniquelyReferenced()
)。
あなたは自由のためのCOWを得ることはありません。独自の構造体はではなく、牛です。 Swiftがstdlibで行うことです。 (再作成する方法については、Mike Ashのgreat discussionを参照してください。)独自のカスタム構造体を渡すと、実際のコピーが作成されます。つまり、ほとんどの構造体のメモリの大部分はコレクションに格納されており、そのコレクションはCOWなので、構造体をコピーするコストは通常かなり低くなります。
本は(それはそれをすべて説明し、それだけで「ちょっと、これはそれが意味するものである」と言って保持しません)スウィフトの値のタイプにドリル多くの時間を費やすことはありません。一方、WWDCでは常に話題になっていました。特にこのトピックについては、Building Better Apps with Value Types in Swiftに興味があります。私はSwift in Practiceもそれについて議論していると信じています。
EDIT2:
@KarlPは、以下のコメントで興味深い点を提起し、それが取り組む価値があります。私たちが議論している価値安全性の約束はどれもfor-in
に関連していません。それらはArray
に基づいています。 for-in
は、コレクションが反復されている間にコレクションを変異させた場合に起こることについて何の約束もしていません。それは意味をなさないだろう。 for-in
は「コレクションを繰り返し処理しません」Generators
のnext()
を呼び出します。したがって、コレクションが変更された場合にGenerator
が未定義になると、Generator
が爆発して、for-in
が爆発します。
func nukeFromOrbit<C: RangeReplaceableCollectionType>(var xs: C) {
var hijack = true
for x in xs {
if hijack {
xs.removeAll()
hijack = false
}
print(x)
}
}
そして、コンパイラはここであなたを助けにはなりません。以下は、あなたが仕様を読んでどのように厳密に応じて、安全ではないかもしれないということを意味
。すべてのSwiftコレクションでうまくいくでしょう。しかし、の突然変異の後にnext()
を呼び出すと、コレクションが未定義の動作である場合、これは未定義の動作です。
私の意見は、そのGenerator
、この場合に未定義になることができますコレクションを作るために貧しいスウィフトだろうということです。もしあなたがそうなら、Generator
仕様を壊してしまったと主張することさえできます(ジェネレータがコピーされていないか、nilを返していない限り、UBは "出"しません)。だから、あなたは上記のコードは完全にスペック内であり、あなたのジェネレータは壊れていると主張することができます。これらの議論は、スウィフトのような "仕様"でちょっと乱雑になりがちですが、これはすべてのコーナーケースに浸透しません。これは、あなたが明確な警告を得ることなくスウィフトで危険なコードを書くことができます
を意味するのでしょうか?絶対に。しかし、実世界のバグを引き起こす多くの場合、Swiftの組み込みの動作は正しいことです。そして、それは他のいくつかのオプションよりも安全です。
コードを実行したところ、出力が期待通りに見えます。 'popFirst()'はディクショナリ上で定義されていません(その順序では定義されていません)。しかし、確かに正しいことをしています。出力は良好に見えますが、エラーの欠落は正しいです。あなたはその考えを持っているように見えます。 –
は前のコメントを無視します。私は、どこにも書かれていない「スナップショットみたいな」振る舞いで投稿を編集しました。あなたがこれについてより正確で公式な説明を提供できるなら、私はあなたにアップアップします。 – KarlP
実際の理由は、* generator *は値型であり、ArrayまたはDictionaryは値型ではないということですか?あるいは、具体的なジェネレータ(IndexingGenerator、DictionaryGenerator)が構造体ですか? SequenceType *プロトコル*が 'generate()'が構造体を返すように強制することは私には分かりません。 (私はちょうど同じ問題を一度疑問に思ったので、私は不思議です:https://forums.developer.apple.com/thread/8551)。 –