すべてのセレクターは、スピードのために、コンパイル時と動的に、実行時に、sel_getUid()
または優先sel_registerName()
(後者が主に優先され、前者は歴史的な理由からまだ使用されています)の両方で一意です。
裏話: メソッドを呼び出すには、ランタイムは、呼び出される対象と呼び出されるオブジェクトを識別するセレクターを必要とします。これが、Objective-C のすべてのメソッド呼び出しに 2 つのパラメーターがある理由です。明らかでよく知られているself
パラメーターと、目に見えない暗黙のパラメーター_cmd
です。 _cmd
現在実行中のメソッドの SEL です。つまり、このコードを任意のメソッドに貼り付けて、現在実行中のメソッドの名前 (セレクター) を確認できます。
NSLog(@"%@", NSStringFromSelector(_cmd));
グローバルで_cmd
はないことに注意してください。それは本当にあなたのメソッドへの引数です。下記参照。
セレクターを一意にすることにより、すべてのセレクター ベースの操作は、文字列処理やポインターの逆参照ではなく、ポインターの等価性テストを使用して実装されます。
特に、メソッド呼び出しを行うたびに:
[someObject doSomething: toThis withOptions: flags]; // calls SEL doSomething:withOptions:
コンパイラは次のコード (または非常に密接に関連するバリアント) を生成します。
objc_msgSend(someObject, @selector(doSomething:withOptions:), toThis, flags);
最初に行うことobjc_msgSend()
は、が nil かどうかを確認し、そうsomeObject
であれば短絡することです (nil-eats-message)。次に (タグ付きポインターを無視して) someObject
s クラスのセレクター (isa
実際にはポインター) を検索し、実装を見つけて、それを呼び出します (末尾呼び出しの最適化を使用)。
実装が高速である必要があり、それを本当に高速にするためには、メソッドの実装をできるだけ高速かつ安定して見つけるための鍵が必要です。そのためには、キーを直接使用でき、プロセスに対してグローバルに一意にする必要があります。
したがって、セレクターは一意です。
たまたまメモリを節約できるということは素晴らしい利点ですが、メッセンジャーが 2 倍高速化された場合、メッセンジャーは現在よりも多くのメモリを使用することになります (ただし、2 倍速の場合は 10 倍ではなく、2 倍速の場合は 2 倍のメモリでさえありません)。重要であり、メモリの使用も重要です、確かに)。
どのように機能するかを深く掘り下げたい場合は、ちょっとしたガイドobjc_msgSend()
を書きました。タグ付きポインタ、blocks-as-implementation、および ARC が公開される前に書かれたため、少し古くなっていることに注意してください。記事を更新する必要があります。