web-dev-qa-db-ja.com

CとObjective-Cでのドット( "。")演算子および矢印( "->")演算子の使用

私は、CとObjective-Cの使用法と構文の違いをいくつか頭に入れようとしています。特に、CとObjective-Cのドット演算子と矢印演算子の使用法がどのように(そしてなぜ)異なるかを知りたいと思います。以下に簡単な例を示します。

Cコード:

// declare a pointer to a Fraction
struct Fraction *frac;

...

// reference an 'instance' variable
int n = (*frac).numerator;     // these two expressions
int n = frac->numerator;       // are equivalent

目的Cコード:

// declare a pointer to a Fraction
Fraction *frac = [[Fraction alloc] init];

...

// reference an instance variable
int n = frac.numerator;        // why isn't this (*frac).numerator or frac->numerator??

したがって、両方のプログラムでfracがどのように同じか(つまり、Fractionオブジェクトまたは構造体へのポインター)を見て、プロパティにアクセスするときに異なる構文を使用するのはなぜですか?特に、Cでは、numeratorプロパティにfrac->numeratorでアクセスしますが、Objective-Cでは、frac.numeratorでドット演算子を使用してアクセスします。 fracは両方のプログラムのポインターであるため、これらの式が異なるのはなぜですか?誰でも私のためにこれを明確にするのを助けることができますか?

61
Charles

fracは、実際には両方のプログラムで同じではありません。

C Fractionstructです。これは、オーバーロードされた演算子を持たない基本型であり、デフォルトでのみ実際に構築および破棄できます。構造体で関数またはフィールドを定義する場合、Cでこれらのプロパティにアクセスするには、ドット(.)演算子を使用します。 Objective-Cは、structsを使用するときにこの演算子を維持します。便宜上、矢印(->)演算子(言及する2つの同等の式)を使用して間接参照とドット演算を実行できます。 Objective-Cは、structsにアクセスするときにこれも保持します。

ただし、あなたの例のObjective-C Fractionはおそらく、少なくともタイプidのポインターです(これは単純にクラス名であり、内部のそのクラスのインスタンスへのポインターです) 。また、NSObjectまたはNSProxyのサブクラスである可能性が非常に高くなります。これらのObjective-Cクラスは、C構造体の上に定義済みの操作のレイヤー全体を持っているという点で特別です(本当に掘り下げたい場合は、 Objective -Cランタイムリファレンス )。また重要な点として、Objective-Cクラスは常にポインターです。

最も基本的な操作の1つは objc_msgSend です。これらのタイプのオブジェクトを操作すると、Objective-Cコンパイラは、ドット(.)演算子または角括弧構文([object method])をobjc_msgSendメソッド呼び出しとして解釈します。ここで実際に起こっていることの詳細については、Bill Bumgarnerによる この一連の投稿 、Obj-Cランタイムの開発を監督するAppleエンジニア。

矢印(->)演算子は、Objective-Cオブジェクトで実際に使用されることを想定していません。前述したように、Objective-Cクラスのインスタンスは追加の通信層が追加されたC構造体ですが、矢印を使用すると、その通信層は基本的にバイパスされます。たとえば、Xcodeを開いて[UIApplication sharedApplication]->と入力し、メソッド完了リストを表示すると、次のように表示されます。

A list of internal ivars on an Obj-C object when using the arrow operator

ここでは、通常角括弧構文([[UIApplication sharedApplication] delegate]など)でアクセスする通常のフィールドの束を見ることができます。ただし、これらの特定のアイテムは、それぞれのObjective-Cプロパティの値を格納するCフィールドです。

だから、おおよそ次のように考えることができます:

Cオブジェクトのドット演算子

  1. (実行時)フィールドの戻り値

Cオブジェクトの矢印演算子(ポインター)

  1. 間接参照ポインター
  2. フィールドの戻り値

Objective-Cオブジェクトのドット演算子/角括弧(ポインター)

  1. (コンパイル時)objc_msgSendの呼び出しに置き換えます
  2. (実行時)Obj-Cクラス定義を検索し、何か問題が発生した場合は例外をスローします
  3. 間接参照ポインター
  4. フィールドの戻り値

Objective-Cオブジェクトの矢印演算子(ポインタ)

  1. (実行時)参照解除ポインター
  2. フィールドの戻り値

ここで間違いなく単純化しすぎていますが、要約すると、矢印演算子はどちらの場合も基本的に同じことをしているように見えますが、Objective-Cではドット演算子は余分な/異なる意味を持っています。

77
darvids0n

ドット表記は設計上の選択です。常にobjcインスタンスへのポインタを扱っているため、デザイナーは既存のプログラムを壊さない、馴染みのあるものを望んでいたと思います。 ObjC 2で導入されました-ほんの数年前。それ以前は、メッセージングには常にブラケットを使用する必要がありました。

ただし、ドット表記は違いをもたらします。直接アクセスではなく、メッセージです。

あれは:

obj.property = val;
// is the same as:
[obj setProperty:val];
// and not:
obj->property = val;


val = obj.property;
// is the same as:
val = [obj property];
// and not:
val = obj->property;

obj->ivarを記述して、オブジェクトのメンバーへのポインターにアクセスできます(表示されている場合)。

9
justin

最初の例では、Fractionは構造体です。 2番目の例では、FractionはObjective-Cクラスです(iOSではおそらくNSObjectのサブクラスになります)。

C++ 許可しないoperator .のオーバーロード。したがって、追加情報がなければ、表示されているドット表記は、C/C++で定義またはオーバーロードされた演算子ではなく、Objective-Cに統合された追加の言語構成であると推測できます。

たまたま、ドット表記は、実装者がプロパティアクセスの省略形として選択した単なる設計機能であり、角括弧ゲッターと完全に同等です。

myObjCVar.prop == [myObjCVar prop];
6
KomodoDave

オブジェクトのドット演算子は、オブジェクトのプロパティにアクセスするための特別な構文です。プロパティのゲッターまたはセッターをバックグラウンドで呼び出します。したがって、たとえば、[@"hello" length]および@"hello".lengthは同等です*。他のすべてのタイプでは、ドットはCドットと同じであり、矢印は常に同じです。

*注:アクセサメソッドの名前は、常にプロパティと同じではありません。宣言されたプロパティで、宣言が特別なgetterまたはsetterメソッドを指定している場合、代わりにそのメソッドが使用されます。

2
Chuck

ドットと矢印の表記は、Objective-Cの場合と同じようにCでも同じです(厳密なスーパーセット)。区別する必要がある基本的な違いは、構造体とObjective-Cオブジェクトの違いだと思います。

Objective-Cのオブジェクトに使用されるドット表記は、Objective-C 2.0で導入されたプロパティに使用されます。ただし、構造体では、Objective-CとCの間の->およびドット表記は同じです。

1
5StringRyan