質問
私はObjective-CとCocoaを学んでいますが、次のステートメントに遭遇しました。
Cocoa フレームワークは、辞書キー、通知名と例外名、および文字列を受け取る一部のメソッド パラメーターに、文字列リテラルではなくグローバル文字列定数が使用されることを想定しています。
私は高級言語でしか仕事をしたことがないので、文字列の詳細をそれほど考慮する必要があったことはありません。文字列定数と文字列リテラルの違いは何ですか?
解決
Objective-C の構文は、 @"foo"
です 不変, リテラル のインスタンス NSString
. 。マイクが想定しているように、文字列リテラルから定数文字列を作成するわけではありません。
通常、Objective-C コンパイラは する コンパイル単位内のリテラル文字列をインターンします。つまり、同じリテラル文字列の複数の使用を結合します。リンカーは、単一のバイナリに直接リンクされているコンパイル単位全体で追加のインターンを行うことができます。(Cocoa は可変文字列と不変文字列を区別し、リテラル文字列も常に不変であるため、これは簡単で安全です。)
絶え間ない 一方、文字列は通常、次のような構文を使用して宣言および定義されます。
// MyExample.h - declaration, other code references this
extern NSString * const MyExampleNotification;
// MyExample.m - definition, compiled for other code to reference
NSString * const MyExampleNotification = @"MyExampleNotification";
ここでの構文演習のポイントは、次のようにできることです。 の用途 文字列のインスタンスが 1 つだけ使用されるようにすることで文字列を効率化します。 複数のフレームワークにまたがる場合でも (共有ライブラリ) 同じアドレス空間内にあります。(配置は const
キーワードは重要です。ポインタ自体が定数であることが保証されます。)
メモリの消費は、8MB の RAM を搭載した 25MHz 68030 ワークステーションの時代ほど大したことではありませんが、文字列が等しいかどうかを比較するのに時間がかかることがあります。ほとんどの場合、等しい文字列がポインタと等しいことを確認すると役立ちます。
たとえば、オブジェクトからの通知を名前でサブスクライブしたいとします。名前に非定数文字列を使用する場合、 NSNotificationCenter
通知を投稿すると、誰がその通知に興味を持っているかを判断する際に、バイトごとの文字列比較が大量に行われる可能性があります。比較対象の文字列が同じポインターを持っているために、これらの比較のほとんどが短絡される場合、それは大きな勝利となる可能性があります。
他のヒント
いくつかの定義
あ リテラル は値であり、定義上不変です。例えば: 10
あ 絶え間ない は読み取り専用の変数またはポインターです。例えば: const int age = 10;
あ 文字列リテラル のような表現です @""
. 。コンパイラはこれを次のインスタンスに置き換えます。 NSString
.
あ 文字列定数 への読み取り専用ポインタです NSString
. 。例えば: NSString *const name = @"John";
最後の行には次のようなコメントがあります。
- それは定数オブジェクトではなく定数ポインタです1.
objc_sendMsg
2 オブジェクトを修飾しても問題ありませんconst
. 。不変オブジェクトが必要な場合は、その不変性をオブジェクト内にコーディングする必要があります3. - 全て
@""
式は確かに不変です。置き換えられます4 コンパイル時に次のインスタンスを使用NSConstantString
, の特殊なサブクラスです。NSString
固定メモリレイアウトの場合5. 。これもその理由を説明していますNSString
コンパイル時に初期化できる唯一のオブジェクトです6.
あ 定数文字列 だろう const NSString* name = @"John";
これは以下と同等です NSString const* name= @"John";
. 。ここでは、構文とプログラマーの意図の両方が間違っています。 const <object>
は無視され、 NSString
実例 (NSConstantString
) はすでに不変でした。
1 キーワード const
apply は、そのすぐ左にあるものに適用されます。左側に何もない場合は、すぐ右側にあるものに適用されます。
2 これは、ランタイムが Objective-C のすべてのメッセージを送信するために使用する関数であり、オブジェクトの状態を変更するために使用できます。
3 例:で const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects];
const は最後のステートメントを妨げません。
4 式を書き換える LLVM コードは次のとおりです。 RewriteModernObjC::RewriteObjCStringLiteral
RewriteModernObjC.cpp 内。
5 見るには NSConstantString
定義を実行するには、Xcode で cmd キーを押しながらクリックします。
6 他のクラスのコンパイル時定数を作成するのは簡単ですが、コンパイラーが特殊なサブクラスを使用する必要があります。これにより、古い Objective-C バージョンとの互換性が失われます。
引用文に戻る
ココアフレームワークは、文字列リテラルではなくグローバル文字列定数が辞書キー、通知、例外名、および文字列を取得するいくつかのメソッドパラメーターに使用されることを期待しています。選択肢がある場合は、常に文字列リテラルよりも文字列定数を常に好む必要があります。文字列定数を使用することにより、スペルをチェックしてランタイムエラーを避けるために、コンパイラの助けを求めます。
リテラルは間違いを起こしやすいと言われています。しかし、速度が遅いとも言えません。比較する:
// string literal
[dic objectForKey:@"a"];
// string constant
NSString *const a = @"a";
[dic objectForKey:a];
2 番目のケースでは、const ポインターを持つキーを使用しているため、代わりに [a isEqualToString:b]
, 、 できます (a==b)
. 。の実装 isEqualToString:
ハッシュを比較してから C 関数を実行します strcmp
, したがって、ポインタを直接比較するよりも遅くなります。それは 定数文字列の方が優れている理由: 比較が速くなり、エラーが発生しにくくなります。
定数文字列もグローバルにしたい場合は、次のようにします。
// header
extern NSString *const name;
// implementation
NSString *const name = @"john";
私の Objective C はまったく存在しないので、C++ を使用しましょう。
文字列を定数変数に隠した場合:
const std::string mystring = "my string";
メソッドを呼び出すときは、my_string を使用し、文字列定数を使用します。
someMethod(mystring);
または、文字列リテラルを使用してこれらのメソッドを直接呼び出すこともできます。
someMethod("my string");
おそらく、文字列定数の使用を推奨する理由は、Objective C が「インターン」を行わないためです。つまり、同じ文字列リテラルを複数の場所で使用すると、それは実際には文字列の別のコピーを指す別のポインターになります。
辞書キーの場合、これは大きな違いを生みます。2 つのポインターが同じものを指していることが確認できれば、文字列の値が等しいことを確認するために文字列全体を比較するよりもはるかにコストが安くなります。
編集: マイク、C# では文字列は不変であり、同じ値を持つリテラル文字列はすべて同じ文字列値を指します。これは不変文字列を持つ他の言語にも当てはまると思います。可変文字列を持つ Ruby では、新しいデータ型が提供されます。記号 (「foo」と:foo、前者は変更可能な文字列、後者はハッシュ キーによく使用される不変の識別子です)。