Objective-Cがプライベートメソッドをサポートしないのはなぜですか?


123

Objective-Cでセミプライベートメソッドを宣言するためのいくつかの戦略を見てきましたが、真にプライベートなメソッドを作成する方法はないようです。私はそれを受け入れます。しかし、なぜそうなのでしょうか。私が本質的に言ったすべての説明は、「あなたはそれを行うことはできませんが、これは厳密な近似です」と述べています。

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


15
いい質問ですね、ところで。
bbum

5
あなたの編集に対して:はい、ランタイムの実施は非常に費用がかかり、ほとんどメリットがありません。コンパイル時の強制は、言語への歓迎すべき追加であり、かなり達成可能です。はい、実行時に回避される可能性がありますが、問題ありません。プログラマーは敵ではありません。スコープの目標は、プログラマーをエラーから保護することです。
Rob Napier

1
「ランタイムをスキップ」することはできません—ランタイムはメッセージのディスパッチを行うものです。
Chuck

「プライベートな方法でこのチェックを短絡させる方法はありません」「回避する」という意味ですか?;-)
コンスタンチノツァロウは

回答:


103

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

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

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

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

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

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

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

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

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

ただし、そうすることは、言語の純粋な動的性質を損なうことになります。すべてのメソッドディスパッチが同じディスパッチメカニズムを通過することはなくなりました。代わりに、ほとんどのメソッドが一方向に動作し、ほんの少しのメソッドが異なるという状況に置かれます。

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


49
+1 Objective-Cは(ある意味で)「ビッグボーイ」言語であり、プログラマーはコンパイラー/ランタイムによって毎回手に負われるのではなく、ある程度の規律を示すことが期待されています。爽やかです。私にとっては、コンパイル/リンク中にメソッドの可視性を制限することで十分であり、好ましいです。
Quinn Taylor

11
より多くのpythonは "obj-c-ic"です:)。Guidoは、PyObjCの最初のバージョンの作成を含め、NeXTシステムでのPythonの保守に非常に積極的でした。したがって、ObjCは多少 Pythonに影響を与えました。
bbum

3
人生の慈悲深い独裁者の興味深い歴史的物語と神話的なNeXTシステムとの交差点の+1。
ポクスタッド2010年

5
ありがとうございました。Python、Java、およびObjective-Cにはすべて、[SmallTalk]に非常に似たランタイムオブジェクトモデルがあります。JavaはObjective-Cから直接派生したものです。Pythonはインスピレーションの1つ以上の並行コースを実行しましたが、Guidoは確かにNeXTSTEPを綿密に研究しました。
bbum '29年

1
私はそのライセンスを信頼しておらず、私は間違いなくgccよりもclangを好みます。しかし、それは確かに良い最初のステップです。
Cthutu

18

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

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

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

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

  2. 別のクラスにHasPrivateは、プライベートインスタンスメソッドもあります。doSomething

  3. あなたは、両方の任意の数のインスタンスを含む配列を作成AllPublicし、HasPrivate

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

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

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


1
費用がかかることに同意します。多分それはあなたがハンドヘルドデバイスで望むものではありません。そうでなければ巨大な修飾子をサポートできますか?デスクトップシステムでは、コストは本当に問題になりますか?
Rob Jones、

1
それはそうではありませんが、あなたは正しいかもしれません。Objective-Cには、ランタイムメッセージディスパッチとC ++のコンパイル時メソッドコールがあります。そのため、コンパイル時チェックとランタイムチェックについて話しています。
Remus Rusanu 2010年

プライベートメソッドをサポートしない主な理由がパフォーマンスに起因することは本当に奇妙に思われます。パフォーマンスに影響があったかどうかにかかわらず、Appleはプライベートメソッドが非常に必要だとは考えていなかったと思います。そうでなければ、彼らは数十億ドル規模の会社のために異なる言語を選択するべきでした。
ポクスタッド2010年

1
プライベートメソッドを追加すると、ランタイムの実装が複雑になるだけでなく、言語のセマンティクスも複雑になります。アクセス制御は静的言語では単純な概念ですが、多くの概念上のオーバーヘッドが発生し、多くの「多くの場合、なぜこれが機能しないのか」につながる動的ディスパッチには不向きです。質問。
Jens Ayton、2010年

7
プライベートな方法で本当に何が買えるの?Objective-Cは、結局のところ、Cベースの言語です。したがって、誰かがプロセスでコードを実行している場合、APIがどれほどプライベートであるか難読化されているかに関係なく、プロセスを簡単にp0wnzできます。
bbum

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


1
この方法の問題は、誰かがクラスをサブクラス化するときに、誰かが(知らないうちに)プライベートメソッドをオーバーライドできることです。基本的に、上書きされる可能性の低い名前を選択する必要があると思います。
dreamlax 2010年

3
私にはハックの上にハックのように感じます。
Rob Jones

1
@マイク:Appleは、先頭にアンダースコアが付いているメソッド名は明示的にAppleのみに予約されていると述べています:developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…。推奨される代替手段は、2つの大文字を前に付け、次に下線を付けることです。
dreamlax 2010年

8
また、メソッドの後にSSNを入れて、他のプログラマーが誰がそれを書いたかわかるようにします。= D名前空間、それらが必要です!!!
ポクスタッド2010年

2
定義したメソッドがオーバーライドされるのではないかと心配している場合は、Objective-Cが推奨する冗長性の固有のレベルを適切に受け入れていません...
Kendall Helmstetter Gelner

7

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

全然大騒ぎしません。


これらは@implementationセクションの外で定義する必要がありますか?
Rob Jones

@Rob-クラスの実装内で定義することはできません(おそらく定義する必要があります)。
Cromulent

6

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

他の手法(例:プレフィックスやアンダースコア)が十分に回避できるコーディング問題空間のかなりの困難を解決することが確立されていないため、これは行われていません。IOW、内在する習慣を克服するには、もっと痛みが必要です。

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


2
コンパイル時の名前のマングリングは、XIBファイルに表示されるものやNSSelectorFromString()のようなものやKeyValueCodingなどに渡されるものも含め、すべてのメソッドセレクターをマングルする必要があるため、想定よりもはるかに困難です...
bbum

1
はい、ツールチェーン全体でプライベートメソッドのサポートを提供する場合は、より複雑になります。コンパイラのシンボルテーブルを保存し、ツールチェーンAPIを介してアクセスできるようにする必要があります。時間と安定性が許す限り十分に重要な機能を開発者に段階的に展開する必要があったのは、これが初めてではありません。ただし、プライベートメソッドが関係している場合、その取り組みを実行するのに十分な勝利であるかどうかは疑問です。これらの他のメカニズムによってクラス自体の外部にアクセスできる必要があることはさらに疑わしいです。
Huperniketes、2010年

1
コンパイル時のみの適用+名前のマングリングは、クラスのメソッドリストを誰かが歩いてそこで見つけないようにする方法を教えてください。
dreamlax 2010

そうではありません。私の答えはOPによって提起された質問のみを扱います。
Huperniketes 2010

私はClangにプライベートメソッドを追加することを試みることを考えました、プライベートメソッドが良いだろうと私が思った1つの理由は、単純なc関数のように直接呼び出すことができ、それから通常のC関数の最適化をすべて行うことができるということです。時間の都合で気にしたことは一度もありませんが、cを使用するだけですでにこれを行うことができます。
Nathan Day

4

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

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


1

これは、Objective-Cのランタイム環境の問題です。C / C ++はコンパイルして判読できないマシンコードにコンパイルされますが、Objective-Cはメソッド名などの一部の人間が読める属性を文字列として維持します。これにより、Objective-Cは反射機能を実行できます。

編集:厳密なプライベートメソッドなしでリフレクティブ言語であることにより、Objective-Cは、呼び出し可能なメソッドを制限するのではなく、コードを使用する他の人々を信頼するという点で、より「パイソン」的になります。二重下線のような命名規則を使用することは、コードをカジュアルなクライアントコーダーから隠すことを意味しますが、より深刻な作業を行う必要があるコーダーを停止することはありません。


そして、もしあなたのlibの消費者があなたのプライベートメソッドを反映できたら、彼らもそれらを呼び出せないでしょうか?
Jared Updike、2010年

彼らがどこを見ればよいのか知っているなら、人々はiPhoneで文書化されていないライブラリをどのように使用してきたのではないでしょうか?iPhoneの問題は、プライベートAPIの使用をアプリに受け入れられないリスクがあることです。
ポクスタッド2010年

リフレクションを介してプライベートメソッドにアクセスする場合、値するものを取得します。エラーはあなたのせいではありません。
gnasher729 2014

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の方法であることを指摘しておきます。


0

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

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

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

たとえば、ヘッダーファイルで宣言する必要があったインスタンス変数(@ private、@ public、および@protected)が使用可能です。


0

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

ある意味で、Smalltalkと同様に、Objective-Cは大​​人のプログラマー向けです。私たちは、元の開発者がインターフェースを想定しているものを理解し、実装を変更する必要がある場合の結果に対処する責任を負います。つまり、それは哲学であり、実装ではありません。


これは反対投票に値しないので、私はこれに賛成票を投じました。ステファンが言うように、「プライベート」メソッドは必要ないという議論があり、それは動的な型と静的な型についての議論に似ています。
alastair 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.