単一の継承では不十分で、問題のドメインをモデル化することが難しい場合、通常、複数の継承を直接考えます。複数の継承とは、アプリケーションをより直接的にモデル化できる、複数の直接基本クラスからクラスを派生させる機能です。ただし、Objective-Cは複数の継承をサポートしていません。メッセージメカニズム名のルックアップはコンパイル時ではなく実行時に行われるため、複数の基本クラスによって引き起こされる可能性のあるあいまいさを解決することは困難です。しかし実際には、Objective-Cは複数の継承をサポートする必要はありません。間接的に複数の継承を実現するには、次の方法があります。
**メッセージ転送****
デリゲートとプロトコル
****カテゴリ**
メッセージ転送
メッセージがsomeObjectに送信されたが、ランタイムシステムが現在のクラスと親クラスで対応するメソッドの実装を見つけることができない場合、ランタイムシステムはプログラムをクラッシュさせるエラーをすぐに報告しませんが、次の手順を順番に実行します。
プロセスを個別に簡単に説明します。
1.動的メソッド分析:resolveInstanceMethod:シグナルを現在のクラスに送信して、メソッドがクラスに動的に追加されているかどうかを確認します。(混乱している場合は、@ dynamicを検索してください)
2。高速メッセージ転送:クラスがforwardingTargetForSelector:メソッドを実装しているかどうかを確認します。実装されている場合は、このメソッドを呼び出します。このメソッドの戻り値オブジェクトがnilまたはselfでない場合、メッセージは戻りオブジェクトに再送信されます。
3.標準メッセージ転送:ランタイムはmethodSignatureForSelector:メッセージを送信して、セレクターに対応するメソッド署名を取得します。戻り値が空でない場合、メッセージはforwardInvocation:を介して転送され、戻り値が空の場合、doesNotRecognizeSelector:メッセージが現在のオブジェクトに送信され、プログラムがクラッシュして終了します。
名前が示すように、上記のプロセスで2と3の方法を使用して、メッセージの転送を完了することができます。
高速メッセージ転送
高速メッセージ転送の実装方法は非常に単純で、書き直す必要があります-(id)forwardingTargetForSelector:(SEL)aSelectorメソッド。
簡単な例を挙げましょう。たとえば、教師と医師の2つの既存のクラスがあります。医師は手術を行うことができます(操作方法)。
@interface Teacher : NSObject
@end
@interface Doctor : NSObject
- (void)operate;
@end
高速メッセージ転送により、教師は医師の手術方法を簡単に呼び出すことができます。
Teacherクラスは、メッセージをDoctorに転送する必要があります。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
Doctor *doctor = [[Doctor alloc]init];
if ([doctor respondsToSelector:aSelector]) {
return doctor;
}
return nil;
}
メッセージは動的に転送および送信できますが、エディターの静的チェックをバイパスすることはできません。問題は、Teacherクラスがoperateメソッドを実装していないため、メッセージをどのように宣言するかです。
これまでのところ、私は次の2つの方法しか考えていませんでした。
宣言方法1 ————カテゴリ
@interface Teacher (DoctorMethod)
- (void)operate;
@end
宣言方法2 ————ヘッダーファイルをインポートし、呼び出すときにタイプ変換を強制します
Teacherヘッダーファイルには、Doctorヘッダーファイルを含める必要があります。これは、Doctor.hでoperatorメソッドの宣言を検索し、呼び出すときにタイプを強制的に変更するようにコンパイラーに指示します。
Teacher *teacher = [[Teacher alloc]init];
[(Doctor *)teacher operate];
興味がある場合は、質問について考えることができます。タイプをIDに変換すると、コンパイルして渡すこともでき、転送を実現できます。しかし、それはどのような隠れた危険をもたらすのでしょうか?
方法1は、明確で単純なカテゴリを使用しているのに、なぜ方法2を提案するのでしょうか。私の考えでは、方法1の欠点は、スローされる方法が固定され、.hで公開されることです。方法2は比較的柔軟性があり、転送したいメッセージを非表示にします。
標準メッセージ転送
標準メッセージ転送では、methodSignatureForSelector:とforwardInvocation:の2つのメソッドを書き直す必要があります。
送信プロセスを次の図に示します。
フォワードリライト方式:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (signature==nil) {
signature = [someObj methodSignatureForSelector:aSelector];
}
NSUInteger argCount = [signature numberOfArguments];
for (NSInteger i=0 ; i
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL seletor = [anInvocation selector];
if ([someObj respondsToSelector:seletor]) {
[anInvocation invokeWithTarget:someObj];
}
}
2つのメッセージ転送方法の比較
高速メッセージ転送:シンプルで高速ですが、転送できるのは1つのオブジェクトのみです。
標準のメッセージ転送:少し複雑で低速ですが、転送操作は制御可能であり、マルチオブジェクト転送を実現できます。
デリゲートプロトコル
委任は、Objective-Cで最も一般的に使用されるコールバックメカニズムです。使用法言うことはあまりないと思います。メカニズムの特徴を要約すると、
委任は本体が操作タスクを完了するのを支援し、カスタマイズが必要な操作は、サブクラス化本体と同様に、実装をカスタマイズするための委任オブジェクト用に予約されています。
また、イベントリスナーとしても使用できます。
しばらくは考えられない…
カテゴリ私は
個人的に、カテゴリはObjective-Cデザインの本質の1つであり、Objective-Cが大好きな最大の理由でもあると思います。
カテゴリは強力なものであり、クラスにメソッドを追加したり、インスタンスを追加したりできます。同意せず、私に思い出させたい人がたくさんいるはずです。カテゴリの制限の1つは、クラスに新しいインスタンス変数を追加できないことです。推薦は本当に台無しです、私が例を挙げて、ゆっくり話すのを聞いてください。
Teacherクラスを再作成しましょう:
@interface Teacher : NSObject
{
NSUInteger age;
}
@end
年齢だけでは教師を説明するのに十分ではありません。教師の職業を保存するために職業インスタンスを追加したいと思います。直感的なアイデアは、Teacherをサブクラス化することです。実際、カテゴリを使用することもできます。
ランタイムプログラミングの知識を理解し、objc_setAssociatedObjectとobjc_getAssociatedObjectに注意を払う必要があります。
//
// Teacher+Profession.m
//
#import "Teacher+Profession.h"
#import
const char *ProfessionType = "NSString *";
@implementation Teacher (Profession)
-(void)setProf:(NSString*)prof
{
objc_setAssociatedObject(self, ProfessionType, prof, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)prof
{
NSString *pro = objc_getAssociatedObject(self, ProfessionType);
return pro;
}
@end
これで、setProf:およびprofを介して教師の職業価値にアクセスできます。
作成者:ScaryMonsterLyn
リンク:https://www.jianshu.com/p/c473b41c083d