123

Objective-Cでセミプライベート メソッドを宣言するための多くの戦略を見てきましたが、真のプライベート メソッドを作成する方法はないようです。私はそれを受け入れます。でも、どうしてこうなるの?私が行ってきたすべての説明は、基本的に「あなたにはできないが、これは近似値だ」と言っています。

(メンバー) に適用され、ivarsそのスコープを制御する多くのキーワードがあります。メソッドに対してもこれを行うことができないのはなぜですか? ランタイムがサポートできるはずの何かのようです。私が見逃している根底にある哲学はありますか?これは意図的なものですか?@private@public@protected

4

10 に答える 10

103

答えは...まあ...簡単です。実際、シンプルさと一貫性。

Objective-C は、メソッド ディスパッチの時点では純粋に動的です。特に、すべてのメソッド ディスパッチは、他のすべてのメソッド ディスパッチとまったく同じ動的メソッド解決ポイントを通過します。実行時に、すべてのメソッド実装はまったく同じ公開を持ち、メソッドとセレクターを操作する Objective-C ランタイムによって提供されるすべての API は、すべてのメソッドで同じように機能します。

多くの人が(ここと他の質問の両方で)答えたように、コンパイル時のプライベートメソッドがサポートされています。クラスが公開されているインターフェイスでメソッドを宣言していない場合、コードに関する限り、そのメソッドは存在しない可能性があります。つまり、プロジェクトを適切に編成することで、コンパイル時に必要な可視性のさまざまな組み合わせをすべて実現できます。

同じ機能をランタイムに複製しても、ほとんどメリットはありません。膨大な量の複雑さとオーバーヘッドが追加されます。そして、そのような複雑さをすべて備えていても、最もカジュアルな開発者以外は、おそらく「プライベート」メソッドを実行できません。

編集: 私が気づいた仮定の 1 つは、プライベート メッセージがランタイムを通過する必要があり、その結果、大きなオーバーヘッドが生じる可能性があるということです。これは絶対に本当ですか?

はい、そうです。クラスの実装者が、実装に含まれる Objective-C の機能セットのすべてを使用したくないと考える理由はありません。つまり、動的ディスパッチが発生する必要があります。 ただし、プライベート メソッドが の特別なバリアントによってディスパッチできなかった特別な理由はありませんobjc_msgSend()。コンパイラはそれらがプライベートであることを認識しているためです。つまり、これは、構造体にプライベート専用メソッド テーブルを追加することで実現できますClass

プライベート メソッドがこのチェックを省略したり、ランタイムをスキップしたりする方法はありませんか?

ランタイムをスキップすることはできませんでしたが、ランタイム必ずしもプライベート メソッドのチェックを行う必要はありません。

objc_msgSendPrivate()そうは言っても、サードパーティがそのオブジェクトの実装の外で意図的にオブジェクトを呼び出すことができない理由はなく、いくつかのもの (KVO など) でそれを行う必要があります。実際には、これは単なる慣例であり、実際には、プライベート メソッドのセレクターにプレフィックスを付けたり、インターフェイス ヘッダーで言及しないよりも少しましです。

ただし、そうすると、言語の純粋に動的な性質が損なわれます。すべてのメソッド ディスパッチが同一のディスパッチ メカニズムを経由することはなくなりました。代わりに、ほとんどのメソッドが 1 つの方法で動作し、ほんの一握りのメソッドが異なるという状況に陥ります。

Objective-C の一貫したダイナミズムの上に構築された Cocoa には多くのメカニズムがあるため、これはランタイムを超えて拡張されます。たとえば、Key Value Coding と Key Value Observation の両方を、プライベート メソッドをサポートするために非常に大幅に変更する必要があります (ほとんどの場合、悪用可能な抜け穴を作成することによって)。そうしないと、プライベート メソッドに互換性がなくなります。

于 2010-01-29T00:18:00.350 に答える
18

ランタイムはそれをサポートできますが、コストは膨大になります。送信されるすべてのセレクターは、そのクラスのプライベートかパブリックかをチェックする必要があります。または、各クラスが 2 つの個別のディスパッチ テーブルを管理する必要があります。このレベルの保護はコンパイル時に行われるため、これはインスタンス変数とは異なります。

また、ランタイムは、プライベート メッセージの送信者が受信者と同じクラスであることを確認する必要があります。プライベート メソッドをバイパスすることもできます。クラスが を使用instanceMethodForSelector:した場合、返さIMPれた を他のクラスに渡して、プライベート メソッドを直接呼び出すことができます。

プライベート メソッドは、メッセージ ディスパッチをバイパスできませんでした。次のシナリオを検討してください。

  1. クラスAllPublicには public インスタンス メソッドがありますdoSomething

  2. 別のクラスHasPrivateには、プライベート インスタンス メソッドも呼び出されます。doSomething

  3. AllPublicとの両方の任意の数のインスタンスを含む配列を作成しますHasPrivate

  4. 次のループがあります。

    for (id anObject in myArray)
        [anObject doSomething];
    

    そのループを 内から実行した場合、ランタイムはインスタンスへのAllPublic送信を停止する必要がありますが、このループがクラス内にある場合は使用できます。doSomethingHasPrivateHasPrivate

于 2010-01-28T23:14:04.903 に答える
13

これまでに投稿された回答は、哲学的な観点から質問に答えるのに適しているので、より実用的な理由を提示します。言語のセマンティクスを変更することで何が得られるでしょうか。プライベートメソッドを効果的に「隠す」のは簡単です。例として、次のように、ヘッダーファイルでクラスが宣言されているとします。

@interface MyObject : NSObject {}
- (void) doSomething;
@end

「プライベート」メソッドが必要な場合は、これを実装ファイルに入れることもできます。

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

確かに、 C ++ / Javaプライベートメソッドとはまったく同じではありませんが、事実上十分に近いので、言語のセマンティクス、コンパイラ、ランタイムなどを変更して、受け入れ可能なものですでにエミュレートされている機能を追加するのはなぜですか?仕方?他の回答で述べられているように、メッセージパッシングセマンティクス(およびランタイムリフレクションへの依存)は、「プライベート」メッセージの処理を重要なものにします。

于 2010-01-28T23:50:24.580 に答える
7

最も簡単な解決策は、Objective-C クラスでいくつかの静的 C 関数を宣言することです。これらは static キーワードの C ルールに従ってファイル スコープのみを持ち、そのため、そのクラスのメソッドでのみ使用できます。

大騒ぎはありません。

于 2010-02-04T11:15:51.627 に答える
6

はい、C ++を処理するためにコンパイラーがすでに採用している手法である名前マングリングを利用することで、ランタイムに影響を与えることなく実行できます。

他の手法(たとえば、接頭辞や下線)が十分に回避できるコーディング問題空間のかなりの困難を解決することが確立されていないため、これは行われていません。IOW、あなたは根付いた習慣を克服するためにもっと痛みが必要です。

構文にプライベートメソッドを追加し、コンパイル中に単独で認識した(そしてすぐに忘れてしまった)マングル名を生成するパッチをclangまたはgccに提供することができます。そうすれば、Objective-Cコミュニティの他のメンバーは、それが実際に価値があるかどうかを判断できるようになります。開発者を説得しようとするよりも、その方が速い可能性があります。

于 2010-01-29T00:58:09.943 に答える
4

基本的に、Objective-Cのメッセージパッシング形式のメソッド呼び出しと関係があります。任意のメッセージを任意のオブジェクトに送信でき、オブジェクトはメッセージへの応答方法を選択します。通常、メッセージにちなんで名付けられたメソッドを実行することで応答しますが、他の多くの方法でも応答する可能性があります。これにより、プライベートメソッドが完全に不可能になるわけではありません— Rubyは同様のメッセージパッシングシステムでそれを行います—しかし、それはそれらをやや厄介にします。

Rubyのプライベートメソッドの実装でさえ、奇妙なために人々を少し混乱させます(このリストにあるものを除いて、オブジェクトに好きなメッセージを送信できます!)。基本的に、Rubyは、プライベートメソッドが明示的なレシーバーで呼び出されることを禁止することで機能させます。Objective-Cにはそのオプションがないため、Objective-Cではさらに多くの作業が必要になります。

于 2010-01-28T23:35:43.830 に答える
1

Objective-C の実行環境の問題です。C/C++は読み取り不能なマシン コードにコンパイルされますが、Objective-C はメソッド名などの人間が読み取れる属性を string として保持します。これにより、Objective-C は反射機能を実行できるようになります。

編集:厳密なプライベート メソッドのないリフレクション言語であることは、Objective-C をより "pythonic" にします。つまり、呼び出すことができるメソッドを制限するのではなく、コードを使用する他の人を信頼するという点です。二重アンダースコアのような命名規則を使用することは、コードをカジュアルなクライアント コーダーから隠すことを目的としていますが、コーダーがより深刻な作業を行う必要があるのを止めることはできません。

于 2010-01-28T23:10:49.893 に答える
1

質問の解釈によって、2つの答えがあります。

1 つ目は、メソッドの実装をインターフェイスから隠すことです。これは通常、名前のないカテゴリで使用されます (例: @interface Foo())。これにより、オブジェクトはそれらのメッセージを送信できますが、他のメッセージは送信できません。

2 番目の答えは、これがパフォーマンスとインライン化に関するものであると仮定して、可能になりますが、代わりにローカル C 関数として行われます。'private foo( NSString *arg)' メソッドが必要な場合は、void MyClass_foo(MyClass *self, NSString *arg)のような C 関数として呼び出しますMyClass_foo(self,arg)。構文は異なりますが、C++ のプライベート メソッドの適切な種類のパフォーマンス特性で動作します。

これは質問への回答ですが、名前のないカテゴリは、これを行うより一般的な Objective-C の方法であることを指摘しておく必要があります。

于 2010-01-29T00:26:52.163 に答える
0

Objective-C はプライベート メソッドを必要としないため、サポートしていません。

C++ では、すべてのメソッドがクラスの宣言で可視でなければなりません。ヘッダー ファイルを含む誰かが見ることができないメソッドを持つことはできません。したがって、実装外のコードで使用してはならないメソッドが必要な場合は、選択の余地がありません。コンパイラーは、そのメソッドを使用してはならないこと、つまり「プライベート」キーワードを伝えることができるツールを提供する必要があります。

Objective-C では、ヘッダー ファイルにないメソッドを使用できます。したがって、メソッドをヘッダー ファイルに追加しないことで、同じ目的を非常に簡単に達成できます。プライベート メソッドは必要ありません。Objective-C には、プライベート メソッドを変更したために、クラスのすべてのユーザーを再コンパイルする必要がないという利点もあります。

以前はヘッダー ファイルで宣言する必要があったインスタンス変数については、@private、@public、および @protected を使用できます。

于 2014-04-12T08:04:43.543 に答える
0

ここで欠けている答えは次のとおりです。プライベート メソッドは、進化性の観点からは悪い考えだからです。メソッドを作成するときにメソッドを非公開にするのは良い考えに思えるかもしれませんが、これは一種の事前バインディングです。コンテキストが変更される可能性があり、後のユーザーが別の実装を使用する場合があります。少し挑発的: 「アジャイル開発者はプライベート メソッドを使用しない」

ある意味、Smalltalk と同じように、Objective-C は大人のプログラマ向けです。私たちは元の開発者が想定していたインターフェースがどうあるべきかを知ることに価値を置いており、実装を変更する必要がある場合はその結果に対処する責任を負います。そうです、それは哲学であり、実装ではありません。

于 2014-07-19T11:32:46.487 に答える