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()を使用する場合は、 open()を使用するだけで、もう一方を使用する場合は、myOpen識別子を介してアクセスします。
Objective-Cでも同様のことが可能ですか?そうでない場合は、名前空間の競合を解決するために使用できる他の巧妙でトリッキーなソリューションはありますか?何か案は?
これを明確にするために、名前空間の衝突を事前に回避する方法や、より良い名前空間を作成する方法を提案する回答を歓迎します。ただし、それらは答えとして受け入れません。なぜなら、彼らは私の問題を解決しないからです。 2つのライブラリがあり、クラス名が衝突しています。それらを変更することはできません。どちらのソースも持っていません。衝突はすでにそこにあり、事前に衝突を回避する方法についてのヒントは役に立たなくなります。これらをこれらのフレームワークの開発者に転送し、将来より良い名前空間を選択することを期待できますが、当面は単一のアプリケーション内でフレームワークを使用するためのソリューションを探しています。これを可能にするソリューションはありますか?
両方のフレームワークのクラスを同時に使用する必要がなく、NSBundleのアンロードをサポートするプラットフォームをターゲットにしている場合(OS X 10.4以降、GNUStepのサポートなし)、パフォーマンスは本当にあなたにとって問題ではありませんクラスを使用する必要があるたびに1つのフレームワークをロードし、次に他のフレームワークを使用する必要があるときにアンロードしてもう1つのフレームワークをロードできること。
私の最初のアイデアは、NSBundleを使用してフレームワークの1つをロードし、そのフレームワーク内のクラスをコピーまたは名前変更してから、他のフレームワークをロードすることでした。これには2つの問題があります。まず、クラスの名前を変更またはコピーするためにポイントされたデータをコピーする関数が見つかりませんでした。名前が変更されたクラスを参照する最初のフレームワークの他のクラスは、他のフレームワークからクラスを参照します。
IMPが指すデータをコピーする方法があれば、クラスをコピーしたり名前を変更したりする必要はありません。新しいクラスを作成してから、ivar、メソッド、プロパティ、およびカテゴリをコピーできます。さらに多くの作業がありますが、可能です。ただし、フレームワーク内の他のクラスが間違ったクラスを参照しているという問題は依然としてあります。
編集:CランタイムとObjective-Cランタイムの基本的な違いは、私が理解しているように、ライブラリがロードされると、それらのライブラリの関数には参照するシンボルへのポインタが含まれますが、Objective-Cでは、 thsoeシンボルの名前。したがって、この例では、dlsymを使用して、メモリ内のシンボルのアドレスを取得し、別のシンボルにアタッチできます。ライブラリ内の他のコードは、元のシンボルのアドレスを変更していないため、引き続き機能します。 Objective-Cは、ルックアップテーブルを使用してクラス名をアドレスにマッピングします。これは1対1のマッピングであるため、同じ名前のクラスを2つ持つことはできません。したがって、両方のクラスをロードするには、一方のクラスの名前を変更する必要があります。ただし、他のクラスがその名前のクラスの1つにアクセスする必要がある場合、ルックアップテーブルにそのアドレスを要求し、ルックアップテーブルは元のクラスの名前が指定された名前変更されたクラスのアドレスを決して返しません。
クラスに一意のプレフィックスを付けることは基本的に唯一のオプションですが、これを面倒で見苦しいものにする方法はいくつかあります。オプションについての長い議論があります here 。私のお気に入りは@compatibility_alias
Objective-Cコンパイラ指令です( here で説明)。 @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)
を使用する必要があります。ビルドフェーズでibtool
を使用して、Interface Builder nib/xibでクラス名を変更し、Interface Builderで完全なCOM_WHATEVER _...を記述する必要がないようにすることができます。
最後の警告:これはコンパイラー指示(およびその点であいまいな指示)であるため、コンパイラー間で移植できない場合があります。特に、LLVMプロジェクトのClangフロントエンドで動作するかどうかはわかりませんが、LLVM-GCC(GCCフロントエンドを使用するLLVM)で動作するはずです。
問題を解決するのに役立つかもしれないトリッキーで賢いコードをすでにいくつかの人々が共有しています。いくつかの提案は機能する可能性がありますが、それらはすべて理想的とは言えず、実装するのは実に厄介です。 (omeいハックは避けられないこともありますが、できる限り回避するようにしています。)実用的な観点から、ここに私の提案があります。
ライセンス料、条件、および期間がこれらのポイントのいずれかでの即時アクションを妨げる可能性があると推測しています。できるだけ早く競合を解決できることを願っています。がんばろう!
これは大したことですが、 分散オブジェクト を使用して、下位プログラムのアドレスとRPCにのみクラスの1つを保持することができます。大量のものをやり取りしている場合は、面倒になります(両方のクラスがビューを直接操作している場合などは不可能な場合があります)。
他の潜在的な解決策もありますが、それらの多くは正確な状況に依存しています。特に、最新のランタイムまたはレガシーランタイムを使用していますか、ファットまたはシングルアーキテクチャ、32ビットまたは64ビット、対象OSリリース、動的リンク、静的リンク、または選択肢がありますか?新しいソフトウェアの更新のためにメンテナンスが必要になる可能性があることをしても大丈夫です。
本当に必死なら、あなたができることは:
上記はかなり手間がかかります。複数のarchと異なるランタイムバージョンに対して実装する必要がある場合は非常に不快になりますが、確実に機能させることができます。
ランタイム関数(/usr/include/objc/runtime.h)を使用して競合するクラスの1つを非衝突クラスに複製し、衝突クラスフレームワークをロードすることを検討しましたか? (これを行うには、衝突するフレームワークを異なる時間にロードして動作させる必要があります。)
ランタイムでクラスivar、メソッド(名前と実装アドレスを含む)、および名前を検査し、同じivarレイアウト、メソッド名/実装アドレスを持ち、名前のみが異なるように独自に動的に作成することができます(衝突)
絶望的な状況は必死の手段を必要とします。ライブラリの1つのオブジェクトコード(またはライブラリファイル)をハッキングし、衝突するシンボルを、同じ長さで異なるスペル(ただし、推奨、同じ長さの名前)の代替名に変更することを検討しましたか?本質的に厄介です。
コードが同じ名前で実装が異なる2つの関数を直接呼び出しているかどうか、または競合が間接的であるかどうかは明確ではありません(違いがあるかどうかも明確ではありません)。ただし、少なくとも名前の変更が機能する外部の可能性があります。スペルの違いを最小限に抑えることも考えられるでしょう。シンボルがテーブル内で並べ替えられた順序にある場合、名前の変更によって順序が変わることはありません。バイナリ検索のようなものは、検索している配列が期待どおりに並べ替えられていない場合、動揺します。
@compatibility_alias
は、クラスの名前空間の競合を解決できます。
@compatibility_alias NewAliasClass OriginalClass;
ただし、これは、enum、typedef、またはプロトコル名前空間の衝突を解決しません。さらに、それは@class
元のクラスの転送宣言。ほとんどのフレームワークにはtypedefなどのこれらの非クラスのものが付属するため、compatibility_aliasだけでは名前空間の問題を修正できない可能性があります。
あなたと同様の問題 を見ましたが、ソースにアクセスでき、フレームワークを構築していました。このために私が見つけた最良の解決策は、@compatibility_alias
条件付きで#definesを使用して、enums/typedefs/protocols/etcをサポートします。問題のヘッダーのコンパイルユニットで条件付きでこれを行うことで、他の衝突するフレームワークでデータを拡張するリスクを最小限に抑えることができます。
問題は、同じ翻訳単位(ソースファイル)で両方のシステムのヘッダーファイルを参照できないことです。ライブラリにObjective-Cラッパーを作成し(プロセスでより使いやすくする)、ラッパークラスの実装に各ライブラリのヘッダーのみを含めると、名前の衝突を効果的に分離できます。
Objective-C(これはまだ始まったばかりです)でこれに関する十分な経験はありませんが、私はCでそれを行うと信じています。
ただの考え..テストも証明もされておらず、マークの方法である可能性がありますが、使用しているクラスのアダプターを、より単純なフレームワークから作成することを検討しましたか。
シンプルなフレームワーク(または、アクセス頻度が最も低いフレームワーク)のラッパーを作成する場合、そのラッパーをライブラリにコンパイルすることはできません。ライブラリがプリコンパイルされており、itsヘッダーのみを配布する必要がある場合、基礎となるフレームワークを効果的に非表示にして、衝突して2番目のフレームワークと自由に組み合わせることができます。
もちろん、両方のフレームワークのクラスを同時に使用する必要がある場合がありますが、そのフレームワークのさらに別のクラスアダプターのファクトリーを提供することもできます。その時点で、両方のフレームワークから使用しているインターフェイスを抽出するには、リファクタリングが少し必要になると思います。これは、ラッパーを構築するための素敵な出発点を提供するはずです。
ラップされたライブラリからさらに機能を必要とするときに、ライブラリに基づいてビルドし、変更されたときに単純に再コンパイルできます。
繰り返しますが、決して証明されていませんが、視点を追加するように感じました。それが役に立てば幸い :)
衝突が発生した場合、フレームワークの1つをアプリケーションからリファクタリングする方法についてよく考えることをお勧めします。衝突があるということは、この2つが同じようなことをしていることを示しており、アプリケーションをリファクタリングするだけで、追加のフレームワークを使用して回避できる可能性があります。これにより、名前空間の問題が解決されるだけでなく、コードの堅牢性、保守性、効率性が向上します。
より技術的な解決策よりも、私があなたの立場にあれば、これが私の選択でしょう。
ファイルのプレフィックスは、私が知っている最も簡単な解決策です。 Cocoadevには、名前空間の衝突を避けるためのコミュニティの取り組みである名前空間ページがあります。このリストにあなた自身を自由に追加してください、私はそれが何のためであると信じています。
衝突が静的リンクレベルでのみ発生する場合、シンボルの解決に使用するライブラリを選択できます。
cc foo.o -ldog bar.o -lcat
foo.o
およびbar.o
両方ともシンボルrat
を参照し、libdog
が解決しますfoo.o
のrat
とlibcat
はbar.o
のrat
。