29

Objective-Cで一度に2つのオブジェクトに委任する方法はありますか?委任パターンは一度に1つの応答を意味し、複数のリスナーとブロードキャストには通知センターがありますが、通知は値を返しません。

ネットワークベースのiOSプロジェクトが多く、複数のリスナーに委任する必要があり、リスナーから値を返す必要がある場合、このシナリオではどのアプローチが最適ですか?

4

6 に答える 6

32

すべてのクラスで代表者は 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になるように編集されました。これは、コメントに記載されているように、保持サイクルを回避するためにデリゲートを弱いポインターで保持する必要があるためです。

于 2013-01-11T13:22:12.507 に答える
20

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
于 2014-05-15T07:08:04.970 に答える
3

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
于 2013-02-22T09:43:51.000 に答える
3

デリゲートを呼び出す関数を作成している場合は、必要な数だけ持つことができます。ただし、デリゲートを呼び出す (変更できない) クラスを使用している場合、クラスがサポートするよりも多くのデリゲートを持つことはできません。

うまくいけば、あるデリゲートに別のデリゲートを呼び出すことができます。最初のデリゲートを設定して、2 番目のデリゲートを呼び出すようにします (そのポインターは最初のデリゲート オブジェクトに格納されます)。これは、Objective-C の動的呼び出しメカニズムを使用して、どの呼び出しが「渡される」かが事前に定義されている単純な場合もあれば、非常に複雑な場合もあります。

于 2013-01-11T13:23:01.570 に答える
3

Robbie Hanson は、マルチキャスト デリゲートの実装を書きました。必要なもののように見えます。彼はここでそれについてより詳細に話し、XMPPFramework でそれがどのように使用されるかを説明します。彼は、主な問題の 1 つについて、いくつかの良い議論をしています。それは、複数のデリゲートが特定のメソッドを実装し、戻り値がクラスの動作を決定する (複数のデリゲートが異なる値を返す) 場合の処理​​方法です。関連ビット:

MulticastDelegate とは何ですか?

xmpp フレームワークは、無制限の数の拡張機能をサポートする必要があります。これには、フレームワークに同梱されている公式の拡張機能と、フレームワークにプラグインする任意の数の拡張機能またはカスタム コードが含まれます。したがって、従来のデリゲート パターンは単純に機能しません。XMPP モジュールと拡張機能は、独自の個別のクラスに分離する必要がありますが、これらの各クラスはデリゲート メソッドを受け取る必要があります。また、標準の NSNotification アーキテクチャも機能しません。これは、これらのデリゲートの一部が戻り変数を必要とするためです。(さらに、通知の userInfo 辞書からパラメーターを抽出するのは本当に面倒です。)

そのため、MulticastDelegate を使用すると、標準のデリゲート パラダイムを使用してフレームワークにプラグインできますが、複数のクラスが同じデリゲート通知を受け取ることができます。この利点は、すべての xmpp 処理コードを 1 つのクラスに入れる必要がないことです。処理を複数のクラスに分割することも、適切と思われる方法で分割することもできます。

于 2013-01-11T14:11:58.720 に答える
1

Bクラスのコールバックを呼び出す場合、およびデリゲートが 1 つしかないCクラスからコールバックを呼び出す場合は、クラスおよびへの参照を持つAデリゲート ラッパーを作成できます。次に、クラスはコールバックを何度も呼び出します。DWrapBCABCDWrap

于 2013-01-11T13:37:50.253 に答える