web-dev-qa-db-ja.com

Swift "mutable"文字列は本当に可変ですか、それともJava文字列と同じですか?

Swiftプログラミング言語、文字列のセクション、サブセクション文字列可変性、それはこれを言います:

特定のStringを変更できるかどうか(またはmutated)を変数に割り当てることで指定します(その場合は変更できます) )、または定数(この場合は変更できません):

サンプルコードを示します。

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified”

IBooksの本 ここ 、またはWebブラウザの本 ここ

次の段落では、「文字列は値型である」と主張しています。

私の質問:それは私には変更可能な文字列のようには見えません。 Java(またはC#、Pythonなど)で慣れているもののように見えます:可変変数バインディングを持つ不変文字列オブジェクト。言い換えると、オブジェクト「Horse」と次に、新しいStringオブジェクト「Horseandcarriage」を作成して同じ変数に設定しました。不変オブジェクトへの参照と値の型の違いを区別する方法がないため(右?)、なぜだろうか。彼らはそれをこのように説明していますか?これらのSwift文字列とJavaでの方法との間に違いはありますか?(またはC#、Python、Objective-C/NSString)

18
Rob N

ある意味で、「可変」と「不変」は、参照型について話すときにのみ意味があります。それを値型に拡張しようとすると、すべての値型は「不変」参照型と機能的に同等であると見なすことができます。

たとえば、タイプvarIntについて考えてみます。これは変更可能ですか?確かに、(=)を割り当てることで、表示される「値」を変更できると言う人もいるかもしれません。ただし、varNSNumberNSStringについても同じことが言えます。これを割り当てることで、表示される値を変更できます。ただし、NSNumberNSString不変クラスとして記述されます。

参照型で実際に起こっていることは、参照型に割り当てると、変数(ポインター)が新しいオブジェクトを指すようになることです。古いオブジェクト自体も新しいオブジェクト自体も「変更」されませんが、別のオブジェクトを指しているため、新しい値が「表示」されます。

クラスが「可変」であると言うときの意味は、オブジェクトのコンテンツを実際に変更するためのAPI(メソッドまたは参照)を提供するということです。しかし、オブジェクトが変更されたことをどうやって知ることができますか? (むしろ、それは新しいオブジェクトですか?)これは、同じオブジェクトへの別の参照を持つことができ、ある参照によるオブジェクトへの変更が別の参照を通じて表示されるためです。ただし、これらのプロパティ(異なるオブジェクトを指し、同じオブジェクトへの複数のポインターを持つ)は、本質的に参照型にのみ適用されます。定義上、値型はそのような「共有」を持つことはできません(「値」の一部がArrayのように参照型でない限り)。したがって、「可変性」の結果は値型では発生しません。

したがって、整数をラップする不変クラスを作成すると、操作上はIntと同等になります。どちらの場合も、変数の値を変更する唯一の方法は、(=)を割り当てることです。それに。したがって、Intも同様に「不変」と見なす必要があります。

Swiftの値型は、メソッドを持つことができ、その一部はmutatingである可能性があるため、少し複雑です。したがって、mutatingメソッドを呼び出すことができる場合値型、それは変更可能ですか?ただし、値型でmutatingメソッドを呼び出すことを、まったく新しい値を割り当てるための構文糖衣と見なすと、これを克服できます(メソッドが変更するものは何でも) )。

14
newacct

Swiftでは、 構造体と列挙型は値型です

実際、Swiftの基本的な型(整数、浮動小数点数、ブール値、文字列、配列、辞書)はすべて値型であり、舞台裏で構造として実装されています。

したがって、文字列は割り当て時にコピーされる値型であり、複数の参照を持つことはできませんが、その基になる文字データは共有可能なコピーオンライトバッファに格納されます。 String構造体のAPIリファレンス は次のように述べています。

Swiftの文字列には値のセマンティクスがありますが、文字列はコピーオンライト戦略を使用してデータをバッファに格納します。このバッファは、文字列のさまざまなコピーで共有できます。文字列のデータ複数の文字列インスタンスが同じバッファを使用している場合、変更時にのみ遅延コピーされます。したがって、変更操作のシーケンスの最初は、時間とスペースがかかる可能性がありますO(n)。

したがって、実際、var vs. letは、不変に見える文字バッファーへの可変対不変のバインディングを宣言します。

var v1 = "Hi"      // mutable
var v2 = v1        // assign/pass by value, i.e. copies the String struct
v1.append("!")     // mutate v1; does not mutate v2
[v1, v2]           // ["Hi!", "Hi"]

let c1 = v1        // immutable
var v3 = c1        // a mutable copy
// c1.append("!")  // compile error: "Cannot use mutating member on immutable value: 'c1' is a 'let' constant"
v3 += "gh"         // mutates v3, allocating a new character buffer if needed
v3.append("?")     // mutates v3, allocating a new character buffer if needed
[c1, v3]           // ["Hi", "High?"]

これは、非最終対final Java 2つのしわのある文字列変数のようなものです。

  1. 変更可能なバインディングを使用すると、変更メソッドを呼び出すことができます。値型へのエイリアスは存在しないため、パフォーマンスへの影響を除いて、変更メソッドが実際に文字バッファーを変更するのか、String変数に再割り当てするのかを判断することはできません。
  2. 実装では、1つのStringインスタンスのみで使用される場合に文字バッファーを所定の位置で変更することにより、変更操作を最適化できます。
4
Jerry101

Swift文字列は値であり、オブジェクトではありません。値を変更すると、別の値になります。したがって、最初のケースでは、varを使用して、同じ変数に新しい値を割り当てるだけです。一方、letは、割り当てた後に他の値を持たないことが保証されているため、コンパイラエラーが発生します。

したがって、あなたの質問に答えるために、Swift文字列はJavaとほぼ同じ扱いを受けますが、オブジェクトではなく値と見なされます。

2
birikino

実際、Swiftの文字列はObjective-C(不変)NSStringのように見えます。あなたがリンクしたドキュメントでこれを見つけました-

SwiftのString型は、FoundationのNSStringクラスにシームレスにブリッジされます。 CocoaまたはCocoaTouchでFoundationフレームワークを使用している場合は、この章で説明する文字列機能に加えて、NSStringAPI全体を使用して作成した任意の文字列値を呼び出すことができます。 NSStringインスタンスを必要とする任意のAPIでString値を使用することもできます。

2
Elliott Frisch

まず、新しい値のインスタンスを作成する不変のメソッドを使用しています。セマンティックを変更する操作を実行するには、mutatingなどのextendメソッドを使用する必要があります。

ここで行ったことは、新しい不変の文字列を作成し、それを既存の名前にバインドすることです。

var variableString = "Horse"
variableString += " and carriage"

これは、余分な名前バインディングなしで文字列をインプレースで変更しています。

var variableString = "Horse"
variableString.extend(" and carriage")

第二に、不変/可変分離の目的は、より簡単で安全なプログラミングモデルを提供することです。不変のデータ構造についてより多くの仮定を安全に行うことができ、多くの頭痛の種を取り除くことができます。そして、これは最適化に役立ちます。不変の型がないと、データを関数に渡すときにデータ全体をコピーする必要があります。そうしないと、元のデータが関数によって変更される可能性があり、この種の影響は予測できません。次に、そのような関数に「この関数は渡されたデータを変更しません」のように注釈を付ける必要があります。不変型を使用すると、関数がデータを変更できないと安全に想定できるため、データをコピーする必要はありません。 Swiftこれは、デフォルトで暗黙的かつ自動的に行われます。

はい、実際には可変/不変の違いは、Swiftのような高級言語のインターフェースの違いにすぎません。値セマンティックは、単にID比較をサポートしていないことを意味します。多くの詳細が抽象化されているため、内部実装は何でもかまいません。 Swiftコードコメントは、文字列がCOWトリックを使用していることを明確にしています。そして、不変/可変インターフェイスの両方が実際にはほとんど不変の実装にマップされていると思います。関係なく、ほぼ同じ結果が得られると思います。しかし、それでも、これは私が言及した不変のデータタイプの利点を提供します。

次に、コード例は実際には同じことを行います。唯一の違いはあなたですオリジナルを変更することはできません不変モードでその名前にバインドされています。

1
Eonil

元の質問に答えるため。 Swiftの文字列は、JavaまたはC#(Pythonについてはわかりません)の文字列と同じではありません。2つの点で異なります。

1)JavaおよびC#の文字列は参照型です。Swift(およびC++)の文字列は値型です。

2)JavaおよびC#の文字列は、参照がfinalまたはreadonlyと宣言されていない場合でも、常に不変です。Swift(およびC++)の文字列は変更可能またはそれらがどのように宣言されているかに応じて不変です(Swiftのletとvar)。

Javaでfinal String str = "foo"を宣言すると、不変のStringオブジェクトへの不変の参照が宣言されます。String str = "foo"は、不変のStringオブジェクトへの不変の参照を宣言します。

let str = "foo"in Swiftを宣言すると、不変の文字列が宣言されます。var str = "foo"は可変の文字列を宣言します。

0
ADG