Objective-Cで一度に2つのオブジェクトに委任する方法はありますか?委任パターンは一度に1つの応答を意味し、複数のリスナーとブロードキャストには通知センターがありますが、通知は値を返しません。
ネットワークベースのiOSプロジェクトが多く、複数のリスナーに委任する必要があり、リスナーから値を返す必要がある場合、このシナリオではどのアプローチが最適ですか?
Objective-Cで一度に2つのオブジェクトに委任する方法はありますか?委任パターンは一度に1つの応答を意味し、複数のリスナーとブロードキャストには通知センターがありますが、通知は値を返しません。
ネットワークベースのiOSプロジェクトが多く、複数のリスナーに委任する必要があり、リスナーから値を返す必要がある場合、このシナリオではどのアプローチが最適ですか?
すべてのクラスで代表者は 1 人であるため、1 人の代表者がイベントについて通知されます。しかし、一連のデリゲートを持つクラスを宣言することを禁止するものは何もありません。
または、代わりに観察を使用します。クラスは複数のクラスによって監視される場合があります。
例
OPから要求されたように、一部のコードも役立つため、これを行う方法は次のとおりです。
@interface YourClass()
@property (nonatomic, strong, readwrite) NSPointerArray* delegates;
// The user of the class shouldn't even know about this array
// It has to be initialized with the NSPointerFunctionsWeakMemory option so it doesn't retain objects
@end
@implementation YourClass
@synthesize delegates;
... // other methods, make sure to initialize the delegates set with alloc-initWithOptions:NSPointerFunctionsWeakMemory
- (void) addDelegate: (id<YourDelegateProtocol>) delegate
{
[delegates addPointer: delegate];
}
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
{
// Remove the pointer from the array
for(int i=0; i<delegates.count; i++) {
if(delegate == [delegates pointerAtIndex: i]) {
[delegates removePointerAtIndex: i];
break;
}
} // You may want to modify this code to throw an exception if no object is found inside the delegates array
}
@end
これは非常に単純なバージョンです。別の方法で行うこともできます。デリゲート セットを公開することはお勧めしません。それがどのように使用されるかはわかりません。特にマルチスレッドでは、一貫性のない状態になる可能性があります。また、デリゲートを追加/削除するときに、追加のコードを実行する必要がある場合があるため、デリゲートをプライベートに設定するのはそのためです。たとえば
、他の多くの方法を使用することもできます。delegatesCount
PS: コードはNSMutableSetではなくNSPointerArrayになるように編集されました。これは、コメントに記載されているように、保持サイクルを回避するためにデリゲートを弱いポインターで保持する必要があるためです。
Ramys の回答に加えて、 a の[NSHashTable weakObjectsHashTable]
代わりに a を
使用できますNSMutableSet
。これにより、デリゲートへの弱い参照のみが保持され、メモリ リークが発生するのを防ぐことができます。標準の弱いデリゲートから既に知っているのと同じ動作が得られます@property (nonatomic, weak) id delegate;
@interface YourClass()
@property (nonatomic, strong) NSHashTable *delegates;
@end
@implementation YourClass
- (instancetype)init
{
self = [super init];
if (self) {
_delegates = [NSHashTable weakObjectsHashTable];
}
return self;
}
- (void) addDelegate: (id<YourDelegateProtocol>) delegate
{
// Additional code
[_delegates addObject: delegate];
}
// calling this method is optional, because the hash table will automatically remove the delegate when it gets released
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
{
// Additional code
[_delegates removeObject: delegate];
}
@end
1 つのデリゲートは 1 つのオブジェクトに対してのみ設定できますが、デリゲートを配列に格納することは可能です。Ramy Al Zuhouri のバリアントは優れていますが、NSArray (NSMutableArray など) クラスは追加されたすべてのオブジェクトを保持しますが、ほとんどの場合デリゲートはretainCount のない割り当てプロパティであるため、配列からデリゲートを解放することは問題になる可能性があると言いたいです。デリゲートを保持すると、デリゲート実装を持つクラスが保持カウント + 1 を持つという結果をもたらす可能性があります。これの解決策は、デリゲート メソッドへのポインタのように NSMutableArray にデリゲートを格納することです。デリゲート ヘッダーを持つシングルトーン クラスを使用しています。
//YourClass.h file
@protocol YourDelegateProtocol <NSObject>
-(void)delegateMethod;
@end
@interface YourClass : NSObject
+(YourClass *)sharedYourClass;
- (void) addDelegate: (id<YourDelegateProtocol>) delegate;
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
@end
//YourClass.m file
@interface YourClass()
@property (nonatomic, retain) NSMutableArray *delegates;
-(void)runAllDelegates;
@end
@implementation YourClass
@synthesize delegates = _delegates;
static YourClass *sharedYourClass = nil;
+(YourClass *)sharedYourClass {
if (!sharedYourClass || sharedYourClass == nil) {
sharedYourClass = [YourClass new];
sharedYourClass.delegates = [NSMutableArray array];
}
return sharedYourClass;
}
-(void)addDelegate: (id<YourDelegateProtocol>) delegate{
NSValue *pointerToDelegate = [NSValue valueWithPointer:delegate];
[_delegates addObject: pointerToDelegate];
}
-(void)removeDelegate: (id<YourDelegateProtocol>) delegate{
NSValue *pointerToDelegate = [NSValue valueWithPointer:delegate];
[_delegates removeObject: pointerToDelegate];
}
-(void)runAllDelegates{
//this method will run all delegates in array
for(NSValue *val in sharedYourClass.delegates){
id<YourDelegateProtocol> delegate = [val pointerValue];
[delegate delegateMethod];
}
}
-(void)dealloc{
sharedYourClass.delegates =nil;
[sharedYourClass release], sharedYourClass =nil;
[super dealloc];
}
@end
//YourClassWithDelegateImplementation.h file
#include "YourClass.h"
@interface YourClassWithDelegateImplementation : NSObject <YourDelegateProtocol>
@end
//YourClassWithDelegateImplementation.m file
@implementation YourClassWithDelegateImplementation
-(id)init{
self = [super init];
if(self){
//...your initialization code
[[YourClass sharedYourClass] addDelegate:self];
}
return self;
}
-(void)delegateMethod{
//implementation of delegate
}
-(void)dealloc{
[[YourClass sharedYourClass] removeDelegate:self];
[super dealloc];
}
@end
デリゲートを呼び出す関数を作成している場合は、必要な数だけ持つことができます。ただし、デリゲートを呼び出す (変更できない) クラスを使用している場合、クラスがサポートするよりも多くのデリゲートを持つことはできません。
うまくいけば、あるデリゲートに別のデリゲートを呼び出すことができます。最初のデリゲートを設定して、2 番目のデリゲートを呼び出すようにします (そのポインターは最初のデリゲート オブジェクトに格納されます)。これは、Objective-C の動的呼び出しメカニズムを使用して、どの呼び出しが「渡される」かが事前に定義されている単純な場合もあれば、非常に複雑な場合もあります。
Robbie Hanson は、マルチキャスト デリゲートの実装を書きました。必要なもののように見えます。彼はここでそれについてより詳細に話し、XMPPFramework でそれがどのように使用されるかを説明します。彼は、主な問題の 1 つについて、いくつかの良い議論をしています。それは、複数のデリゲートが特定のメソッドを実装し、戻り値がクラスの動作を決定する (複数のデリゲートが異なる値を返す) 場合の処理方法です。関連ビット:
MulticastDelegate とは何ですか?
xmpp フレームワークは、無制限の数の拡張機能をサポートする必要があります。これには、フレームワークに同梱されている公式の拡張機能と、フレームワークにプラグインする任意の数の拡張機能またはカスタム コードが含まれます。したがって、従来のデリゲート パターンは単純に機能しません。XMPP モジュールと拡張機能は、独自の個別のクラスに分離する必要がありますが、これらの各クラスはデリゲート メソッドを受け取る必要があります。また、標準の NSNotification アーキテクチャも機能しません。これは、これらのデリゲートの一部が戻り変数を必要とするためです。(さらに、通知の userInfo 辞書からパラメーターを抽出するのは本当に面倒です。)
そのため、MulticastDelegate を使用すると、標準のデリゲート パラダイムを使用してフレームワークにプラグインできますが、複数のクラスが同じデリゲート通知を受け取ることができます。この利点は、すべての xmpp 処理コードを 1 つのクラスに入れる必要がないことです。処理を複数のクラスに分割することも、適切と思われる方法で分割することもできます。
B
クラスのコールバックを呼び出す場合、およびデリゲートが 1 つしかないC
クラスからコールバックを呼び出す場合は、クラスおよびへの参照を持つA
デリゲート ラッパーを作成できます。次に、クラスはコールバックを何度も呼び出します。DWrap
B
C
A
B
C
DWrap