私は、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
は両方のプログラムのポインターであるため、これらの式が異なるのはなぜですか?誰でも私のためにこれを明確にするのを助けることができますか?
frac
は、実際には両方のプログラムで同じではありません。
C Fraction
はstruct
です。これは、オーバーロードされた演算子を持たない基本型であり、デフォルトでのみ実際に構築および破棄できます。構造体で関数またはフィールドを定義する場合、C
でこれらのプロパティにアクセスするには、ドット(.
)演算子を使用します。 Objective-Cは、struct
sを使用するときにこの演算子を維持します。便宜上、矢印(->
)演算子(言及する2つの同等の式)を使用して間接参照とドット演算を実行できます。 Objective-Cは、struct
sにアクセスするときにこれも保持します。
ただし、あなたの例の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]->
と入力し、メソッド完了リストを表示すると、次のように表示されます。
ここでは、通常角括弧構文([[UIApplication sharedApplication] delegate]
など)でアクセスする通常のフィールドの束を見ることができます。ただし、これらの特定のアイテムは、それぞれのObjective-Cプロパティの値を格納するC
フィールドです。
だから、おおよそ次のように考えることができます:
Cオブジェクトのドット演算子
Cオブジェクトの矢印演算子(ポインター)
Objective-Cオブジェクトのドット演算子/角括弧(ポインター)
objc_msgSend
の呼び出しに置き換えますObjective-Cオブジェクトの矢印演算子(ポインタ)
ここで間違いなく単純化しすぎていますが、要約すると、矢印演算子はどちらの場合も基本的に同じことをしているように見えますが、Objective-Cではドット演算子は余分な/異なる意味を持っています。
ドット表記は設計上の選択です。常に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
を記述して、オブジェクトのメンバーへのポインターにアクセスできます(表示されている場合)。
最初の例では、Fraction
は構造体です。 2番目の例では、Fraction
はObjective-Cクラスです(iOSではおそらくNSObject
のサブクラスになります)。
C++ 許可しないoperator .
のオーバーロード。したがって、追加情報がなければ、表示されているドット表記は、C/C++で定義またはオーバーロードされた演算子ではなく、Objective-Cに統合された追加の言語構成であると推測できます。
たまたま、ドット表記は、実装者がプロパティアクセスの省略形として選択した単なる設計機能であり、角括弧ゲッターと完全に同等です。
myObjCVar.prop == [myObjCVar prop];
オブジェクトのドット演算子は、オブジェクトのプロパティにアクセスするための特別な構文です。プロパティのゲッターまたはセッターをバックグラウンドで呼び出します。したがって、たとえば、[@"hello" length]
および@"hello".length
は同等です*。他のすべてのタイプでは、ドットはCドットと同じであり、矢印は常に同じです。
*注:アクセサメソッドの名前は、常にプロパティと同じではありません。宣言されたプロパティで、宣言が特別なgetterまたはsetterメソッドを指定している場合、代わりにそのメソッドが使用されます。
ドットと矢印の表記は、Objective-Cの場合と同じようにCでも同じです(厳密なスーパーセット)。区別する必要がある基本的な違いは、構造体とObjective-Cオブジェクトの違いだと思います。
Objective-Cのオブジェクトに使用されるドット表記は、Objective-C 2.0で導入されたプロパティに使用されます。ただし、構造体では、Objective-CとCの間の->およびドット表記は同じです。