176

Objective-C には名前空間がありません。C によく似ており、すべてが 1 つのグローバル名前空間内にあります。一般的な慣行は、クラスに頭文字を付けることです。たとえば、IBM で働いている場合は、頭に「IBM」を付けることができます。Microsoft で働いている場合は、「MS」を使用できます。等々。イニシャルがプロジェクトを指す場合もあります。たとえば、Adium はクラスの前に「AI」を付けます (その背後にイニシャルを使用できる会社がないため)。Apple はクラスに NS という接頭辞を付け、この接頭辞は Apple 専用に予約されていると述べています。

ここまでは順調。しかし、クラス名の先頭に 2 ~ 4 文字を追加することは、非常に限られた名前空間です。たとえば、MS または AI はまったく異なる意味を持つ可能性があり (たとえば、AI は人工知能である可能性があります)、他の開発者がそれらを使用して同じ名前のクラスを作成することを決定する場合があります。Bang、名前空間の衝突。

これが自分のクラスの 1 つと、使用している外部フレームワークの 1 つとの間の衝突である場合は、クラスの名前を簡単に変更できます。大したことではありません。しかし、2 つの外部フレームワークを使用していて、どちらのフレームワークもソースがなく、変更できない場合はどうなるでしょうか? アプリケーションがそれらの両方とリンクすると、名前の競合が発生します。これらを解決するにはどうすればよいでしょうか?両方のクラスを引き続き使用できるように、それらを回避する最善の方法は何ですか?

C では、ライブラリに直接リンクしないことでこれらを回避できます。代わりに、dlopen() を使用して実行時にライブラリをロードし、dlsym() を使用して探しているシンボルを見つけ、それをグローバル シンボルに割り当てます (任意の方法で名前を付けることができます)、このグローバル シンボルを介してアクセスします。たとえば、一部の C ライブラリに open() という名前の関数があるために競合が発生した場合、myOpen という名前の変数を定義して、ライブラリの open() 関数を指すようにすることができます。 、open() を使用するだけで、もう一方を使用する場合は、myOpen 識別子を介してアクセスします。

Objective-Cで同様のことが可能ですか?そうでない場合、名前空間の競合を解決するために使用できる巧妙でトリッキーなソリューションは他にありますか? 何か案は?


アップデート:

これを明確にするために、名前空間の衝突を事前に回避する方法や、より良い名前空間を作成する方法を提案する回答は大歓迎です。ただし、私の問題を解決しないため、回答として受け入れません。2 つのライブラリがあり、それらのクラス名が衝突しています。それらを変更することはできません。どちらのソースも持っていません。衝突はすでに発生しており、事前に回避できた方法に関するヒントはもはや役に立ちません。それらをこれらのフレームワークの開発者に転送し、彼らが将来より良い名前空間を選択することを願っていますが、当分の間、単一のアプリケーション内で現在フレームワークを操作するソリューションを探しています. これを可能にする解決策はありますか?

4

13 に答える 13

93

クラスに一意のプレフィックスを付けることが基本的に唯一のオプションですが、これを面倒で見苦しくする方法はいくつかあります。オプションについては、ここで長い議論があります。私のお気に入りは、@compatibility_aliasObjective-C コンパイラ ディレクティブです (ここで説明します)。を使用@compatibility_aliasしてクラスの「名前を変更」し、FQDN またはそのようなプレフィックスを使用してクラスに名前を付けることができます。

@interface COM_WHATEVER_ClassName : NSObject
@end

@compatibility_alias ClassName COM_WHATEVER_ClassName
// now ClassName is an alias for COM_WHATEVER_ClassName

@implementation ClassName //OK
//blah
@end

ClassName *myClass; //OK

完全な戦略の一部として、すべてのクラスに FQDN などの一意のプレフィックスを付けてから、すべてのヘッダーを作成でき@compatibility_aliasます (ヘッダーを自動生成できると思います)。

このようなプレフィックスの欠点はCOM_WHATEVER_ClassName、コンパイラ以外の文字列からクラス名を必要とするものには、真のクラス名 (上記など) を入力する必要があることです。特に、@compatibility_aliasランタイム関数ではなくコンパイラ ディレクティブであるため、NSClassFromString(ClassName)失敗します ( が返さnilれます)-- を使用する必要がありますNSClassFromString(COM_WHATERVER_ClassName)。via build フェーズを使用ibtoolして Interface Builder nib/xib のクラス名を変更できるため、Interface Builder で完全な COM_WHATEVER_... を記述する必要はありません。

最後の注意点: これはコンパイラ ディレクティブ (およびあいまいなもの) であるため、コンパイラ間で移植できない場合があります。特に、LLVM プロジェクトの Clang フロントエンドで動作するかどうかはわかりませんが、LLVM-GCC (GCC フロントエンドを使用する LLVM) では動作するはずです。

于 2008-10-07T22:17:27.113 に答える
48

両方のフレームワークのクラスを同時に使用する必要がなく、NSBundle のアンロードをサポートするプラットフォーム (OS X 10.4 以降、GNUStep のサポートなし) をターゲットとしていて、パフォーマンスが実際に問題にならない場合、私は信じています。フレームワークからクラスを使用する必要があるたびにフレームワークをロードし、それをアンロードして、他のフレームワークを使用する必要があるときに別のフレームワークをロードできます。

私の最初のアイデアは、NSBundle を使用してフレームワークの 1 つをロードし、そのフレームワーク内のクラスをコピーまたは名前変更してから、他のフレームワークをロードすることでした。これには 2 つの問題があります。まず、クラスの名前変更またはコピーを指すデータをコピーする関数が見つかりませんでした。名前が変更されたクラスを参照する最初のフレームワーク内の他のクラスは、他のフレームワークからクラスを参照するようになりました。

IMP が指すデータをコピーする方法があれば、クラスをコピーしたり名前を変更したりする必要はありません。新しいクラスを作成してから、ivar、メソッド、プロパティ、およびカテゴリをコピーできます。より多くの作業が必要ですが、可能です。ただし、フレームワーク内の他のクラスが間違ったクラスを参照しているという問題は依然としてあります。

編集: C ランタイムと Objective-C ランタイムの基本的な違いは、私が理解しているように、ライブラリが読み込まれると、これらのライブラリの関数には、参照するシンボルへのポインタが含まれますが、Objective-C では、オブジェクトの文字列表現が含まれます。これらのシンボルの名前。したがって、この例では、dlsym を使用してメモリ内のシンボルのアドレスを取得し、それを別のシンボルにアタッチできます。元のシンボルのアドレスを変更していないため、ライブラリ内の他のコードは引き続き機能します。Objective-C はルックアップ テーブルを使用してクラス名をアドレスにマップします。これは 1 対 1 のマッピングであるため、同じ名前の 2 つのクラスを持つことはできません。したがって、両方のクラスをロードするには、いずれかの名前を変更する必要があります。ただし、他のクラスがその名前のクラスの 1 つにアクセスする必要がある場合、

于 2008-10-08T04:51:59.030 に答える
12

何人かの人々が、問題を解決するのに役立つかもしれないトリッキーで巧妙なコードをすでに共有しています。いくつかの提案はうまくいくかもしれませんが、それらはすべて理想的とは言えず、実装するのがまったく厄介なものもあります. (見苦しいハックが避けられない場合もありますが、できる限り避けるようにしています。) 実用的な観点から、ここに私の提案を示します。

  1. いずれにせよ、開発者に競合の両方のフレームワークを通知し、競合を回避および/または対処することを怠ったことが実際のビジネス上の問題を引き起こしていることを明確にします。クラスごとに既存の競合を解決することはそれほど邪魔にならない修正ですが、それらのプレフィックスを完全に変更する (または、現在変更していない場合はそれを使用してください) ことは、競合が起こらないようにする最善の方法であることを強調してください。もう一度同じ問題を見てください。
  2. 名前の競合がかなり小さなクラスのセットに限定されている場合、特に競合するクラスの 1 つがコードで直接的または間接的に使用されていない場合は、それらのクラスだけを回避できるかどうかを確認してください。その場合は、競合するクラスを含まないフレームワークのカスタム バージョンをベンダーが提供するかどうかを確認してください。そうでない場合は、その柔軟性のなさがフレームワークの使用による ROI を低下させているという事実について率直に言ってください。理にかなった強引さを気にする必要はありません。顧客は常に正しいのです。;-)
  3. あるフレームワークがより「不要」である場合は、サードパーティまたは自作の別のフレームワーク (またはコードの組み合わせ) に置き換えることを検討できます。(後者は望ましくない最悪のケースです。開発と保守の両方で追加のビジネス コストが確実に発生するためです。) その場合は、そのフレームワークのベンダーに、そのフレームワークを使用しないことに決めた理由を正確に伝えてください。
  4. 両方のフレームワークがアプリケーションに等しく不可欠であると見なされる場合は、Louis Gerbarg が提案したように、おそらく DO を介して通信する、1 つまたは複数の個別のプロセスにそれらのいずれかの使用を除外する方法を検討してください。コミュニケーションの程度によっては、これは思ったほど悪くないかもしれません。いくつかのプログラム (QuickTime を含むと思います) は、このアプローチを使用して、Leopard の Seatbelt サンドボックス プロファイルを使用することで提供される、よりきめ細かなセキュリティを提供します。これにより、コードの特定のサブセットのみが重要または機密性の高い操作の実行を許可されます。パフォーマンスはトレードオフになりますが、それが唯一の選択肢かもしれません

ライセンス料、条件、および期間により、これらの点のいずれかに対する即時の行動が妨げられる可能性があると推測しています. 紛争をできるだけ早く解決できることを願っています。幸運を!

于 2009-06-16T17:43:34.433 に答える
4

ランタイム関数 (/usr/include/objc/runtime.h) を使用して、衝突するクラスの 1 つを衝突しないクラスに複製し、衝突するクラス フレームワークをロードすることを検討しましたか? (これには、衝突するフレームワークを異なる時間にロードして機能させる必要があります。)

クラス ivar、メソッド (名前と実装アドレスを含む)、および名前をランタイムで検査し、独自のものを動的に作成して、同じ ivar レイアウト、メソッド名/実装アドレスを持ち、名前だけが異なるようにすることができます (衝突)

于 2008-12-07T18:35:53.323 に答える
3

絶望的な状況には絶望的な対策が必要です。ライブラリの 1 つのオブジェクト コード (またはライブラリ ファイル) をハッキングして、衝突するシンボルを別の名前に変更することを検討しましたか? 同じ長さでスペルが異なります (ただし、名前の長さが同じであることが推奨されます)。本質的に厄介です。

コードが同じ名前で実装が異なる 2 つの関数を直接呼び出しているのか、それとも競合が間接的なものなのかは明らかではありません (違いがあるかどうかも明らかではありません)。ただし、名前の変更が機能する可能性は少なくともあります。スペルの違いを最小限に抑えることも考えられるため、シンボルがテーブル内でソートされた順序になっている場合、名前の変更によって順序が乱れることはありません。検索している配列が期待どおりにソートされていない場合、二分探索のようなものは動揺します。

于 2009-02-27T07:49:06.813 に答える
0

単なる考え..テストまたは証明されておらず、マークの方法である可能性がありますが、より単純なフレームワークから使用するクラスのアダプターを作成することを検討しましたか..または少なくともそれらのインターフェース?

より単純なフレームワーク (またはアクセスするインターフェイスが最も少ないフレームワーク) の周りにラッパーを作成する場合、そのラッパーをライブラリにコンパイルすることはできません。ライブラリがプリコンパイルされており、そのヘッダーのみを配布する必要がある場合、基礎となるフレームワークを効果的に隠し、2 番目のフレームワークと自由に組み合わせて衝突させることができます。

もちろん、両方のフレームワークのクラスを同時に使用する必要がある場合もあると思いますが、そのフレームワークの追加のクラス アダプターにファクトリを提供できます。その点を踏まえると、両方のフレームワークから使用しているインターフェイスを抽出するには、少しリファクタリングが必要になると思います。これは、ラッパーを構築するための優れた出発点を提供するはずです。

ラップされたライブラリからさらに機能が必要なときにライブラリを構築し、変更時に再コンパイルするだけです。

繰り返しますが、決して証明されたわけではありませんが、視点を追加するように感じました. それが役に立てば幸い :)

于 2013-05-31T16:01:52.787 に答える
0

ファイルのプレフィックスは、私が知っている最も簡単な解決策です。Cocoadev には、名前空間の衝突を回避するためのコミュニティの取り組みである名前空間ページがあります。このリストに自由に独自のものを追加してください。それが目的だと思います。

http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix

于 2008-10-07T14:14:04.560 に答える
0

衝突が静的リンク レベルでのみ発生する場合は、シンボルの解決に使用するライブラリを選択できます。

cc foo.o -ldog bar.o -lcat

foo.oとのbar.o両方がシンボルratを参照する場合、libdogは を解決し、は を解決しfoo.oます。ratlibcatbar.orat

于 2012-05-20T03:27:45.863 に答える
-1

同じ関数名を持つ 2 つのフレームワークがある場合は、フレームワークを動的にロードしてみてください。エレガントではありませんが、可能です。Objective-C クラスでそれを行う方法はわかりません。クラスには、特定のNSBundleクラスをロードするメソッドがあると思います。

于 2013-01-18T00:54:49.267 に答える