web-dev-qa-db-ja.com

static const Vs extern const

私はヘッダーファイルで静的constを次のように使用しています:

static NSString * const myString = @"foo";

しかし、これは「安全な」またはこれを行う正しい方法ではないことを読んでください。どうやら、const文字列に別のクラスからアクセスしたい場合は、.hで文字列を次のように宣言する必要があります。

extern NSString * const myString;

次に、私の.mファイルで:

NSString * const myString = @"foo";

これは正しいです?もしそうなら、私の.hファイルで直接静的として宣言しない理由は何ですか?それは完全にうまく機能し、これに関する「安全性」の問題は見当たりません。これはconstであるため、外部から変更することはできず、クラスの外部で意図的にアクセスする必要があります。私が考えることができる他の唯一のことは、文字列の値を非表示にすることですか?

20
ssh88

あなたの最初の変種

_static NSString * const myString = @"foo"; // In .h file, included by multiple .m files
_

ヘッダーファイルを含む各「翻訳ユニット」(大まかに言えば:各.mソースファイル)でローカルにmyString変数を定義します。すべての文字列オブジェクトの内容は「foo」と同じですが、オブジェクトが異なる可能性があるため、myString(文字列オブジェクトへのポインタ)の値は次のようになります。ユニットごとに異なります。

2番目のバリアント

_extern NSString * const myString; // In .h file, included by multiple .m files
NSString * const myString = @"foo"; // In one .m file only
_

「グローバルに」表示されるsingle変数myStringを定義します。

例: 1つのクラスで、ユーザーオブジェクトとしてmyStringを使用して通知を送信します。別のクラスでは、この通知が受信され、ユーザーオブジェクトがmyStringと比較されます。

最初のバリアントでは、送信クラスと受信クラスのポインタが異なる可能性があるため_isEqualToString:_との比較を行う必要があります(両方ともNSStringを指します)。内容が「foo」のオブジェクト)。したがって、_==_との比較は失敗する可能性があります。

2番目のバリアントでは、myString変数が1つしかないため、_==_と比較できます。

したがって、2番目のバリアントはより安全であり、「共有文字列」は各変換単位の同じオブジェクトであるという意味です。

36
Martin R

プロジェクトでのみObjective-Cを使用しているのに、Objective-Cで何かを外部として宣言する理由はありません。 Cやアセンブラモジュールなどと組み合わせると理由が思い浮かびます。

ただし、externには、プロジェクト全体で定数が実際に存在するのは1つだけであるという利点があります。これが達成したい場合、これらの20バイト程度を本当に節約する必要がある場合です。しかし、それは名前が矛盾するリスクを伴います。他のライブラリは、同じ名前を使用して独自の外部を宣言している可能性があります。また、リンカは、タイプが異なる場合でも、メモリ内のまったく同じスペースを使用してそれらを処理します。

はい、ヘッダーのextern宣言には、.mファイルの対応する定義を付ける必要があります。よくわかりませんが、.hファイルに@ "foo"を割り当てることができると思います。 @ interface/@ implementation- @ endブロックの外で宣言することもできます。 (自分で試したことはありません)。その場合、変数はグローバルであり、externキーワードがなくてもどこからでもアクセスできます。コンパイル時に、コンパイラは、#includeステートメントのチェーン内に宣言が表示されない場合、それらへのアクセスについて文句を言います。しかし、学術的には、1つの.mファイルに2つ以上のクラスが含まれている可能性があり(私は明らかにお勧めしません)、変数はどちらにも属していないものの、両方のクラスからアクセスできます。結局のところ、OjbCはANSICへの単なるアドオンです。

ただし、それらを静的にすることには意味がありません。これらの定数はとにかく静的です。それらは定数です。クラスまたはメソッド内の静的変数の目的は、そのスコープ(可視性)がそのクラスに制限されることですが、実行時にクラスのすべてのインスタンスによって共有されるインスタンスは1つだけです。

例:

@implementation AClass : NSObject 

static NSString *someString 

- (void) setString:(NSString*) aString{
  someString = aString;
}

- (NSString*) getString (){
  return someString;
}

...そしてどこか他の場所:

AClass * a = [[AClass alloc] init];
AClass * b = [[AClass alloc] init]; 

[a setString:@"Me"];
[b setString;@"You"];
NSLog (@"String of a: ", [a getString]);

Youは出力されますが、Meは出力されません

それが必要な場合は、静的を使用してください。

単純なプリプロセッサマクロを使用すると(私は好きですが、ここではちょっと古い学校です)、マクロが使用されるたびにこれらの文字列がバイナリにコピーされるという欠点があります。どうやらそれはあなたがそれらを求めさえしなかったのでとにかくあなたのためのオプションではありません。ただし、ほとんどの使用法では、一般的に共有される.hファイルのプリプロセッサマクロは、クラス間で定数を管理するトリックを実行します。

3
Hermann Klecker

外部方法の利点は、他の回答に記載されています。

変数がオブジェクトではなく(あなたの場合はNSString)、プリミティブ型(のような)である場合の静的な方法の1つの可能な利点整数)は、コンパイラが定数へのアクセスを最適化できることです。

プログラムでconstを宣言すると、

int const x = 2;

コンパイラは、この変数にストレージを提供せず、シンボルテーブルに追加することで、この定数を最適化できます。したがって、後続の読み取りでは、メモリから値をフェッチするための命令ではなく、シンボルテーブルへの間接参照が必要です。

この回答 から取得。

1
Franklin Yu

ヘッダーファイルで_static NSString* const myString = @"foo";_を使用することは、各変換ユニットが個別のmyString変数を取得することを意味します。リンカーmayこれらを統合すると思いますが、私はそれを当てにしません。つまり、呼び出し元が別の変換ユニットからのものである場合、呼び出し元がmyStringを渡した場合でも、if (someString == myString) ...を使用して受信した文字列を比較するコードはfalseになる可能性があります。 (もちろん、コードは_-isEqualToString:_の代わりに_==_を使用する必要がありますが、適切に宣言された文字列定数を使用すると、後者が機能する場合があります。)

1
Ken Thomases

しかし、これは「安全」またはこれを行うことに対する正しい警戒ではないことを読んでください。

プログラムがマルチスレッドでない限り、安全です。その場合、ミューテックスでグローバル変数を保護しない限り、安全ではありません。

しかし、それは正しくありません。 NSString * const myStringは、(非定数)データへの定数ポインターを意味します。ほとんどの場合、変数自体を定数にする必要があります。const NSString* myString

どうやら、const文字列に別のクラスからアクセスしたい場合は、.hで文字列を次のように宣言する必要があります:extern .. ..

正しい。 externは定数に対してのみ受け入れられることに注意してください。非定数変数には受け入れられません。グローバル変数は非常に悪い習慣と見なされます。

もしそうなら、それが完全にうまく機能するので、それを私の.hファイルで直接静的として宣言するだけで使用しない理由は何ですか?

静的として宣言したい唯一の理由は、変数のスコープをローカルの.cファイル、つまりprivate encapsulationに制限したいからです。したがって、.hファイルで静的変数を宣言することは意味がありません。

Constなので、外部から変更することはできません

Constではないため、変更できます。最初のコメントを参照してください。


一般的に、このようなことはしないでください。上記のすべてのいじりは、プログラム設計に欠陥があり、修正する必要があることを示唆しています。すべてのコードモジュールが自律的であり、指定されたタスク以外は何も知らない、または気にしないように、オブジェクト指向の方法でプログラムを設計する必要があります。

0
Lundin

ストレージクラスに関して言えば、静的とは2つのことのいずれかを意味します。

メソッドまたは関数内の静的変数は、呼び出し間でその値を保持します。

グローバルに宣言された静的変数は、それらの関数が静的変数と同じファイルにある限り、任意の関数またはメソッドで呼び出すことができます。同じことが静的関数にも当てはまります。

Staticは関数と変数を特定のファイル内でグローバルに表示しますが、externはそれらをすべてのファイルでグローバルに表示します。

アプリケーションがパブリックインターフェイスで非言語値の文字列定数を使用する場合は常に、それを外部文字列定数として宣言する必要があります。

このパターンは、パブリックヘッダーでextern NSString * constを宣言し、実装でそのNSString * constを定義することです。

ソース: nshipster.com/c-storage-classes

0
Tariq