Подтвердить что ты не робот

Делегирование на несколько объектов

Есть ли способ делегировать два объекта за раз в Objective-C? Я знаю, что диаграмма делегирования подразумевает один ответ за раз, а для нескольких слушателей и трансляции есть центр уведомлений, но уведомление не возвращает никакого значения.

Если у меня есть сильно основанный на сети проект iOS и нужно делегировать нескольким слушателям и необходимо возвращать значения из них, в этом сценарии какой подход должен быть лучшим?

4b9b3361

Ответ 1

В каждом классе делегат один, поэтому один делегат информируется о событии. Но ничто не запрещает вам объявлять класс с набором делегатов.

Или используйте Observation вместо этого. Класс может наблюдаться несколькими классами.

Пример

Как запрошено 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: Код был отредактирован как NSPointerArray, а не NSMutableSet, потому что, как указано в комментариях, делегат должен удерживаться со слабым указателем, чтобы избежать сохранения циклов.

Ответ 2

В дополнение к ответу Рамса вы можете использовать [NSHashTable weakObjectsHashTable] вместо 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

Ответ 3

Один делегат может устанавливать только для одного объекта, но он может хранить делегаты в массиве. Вариант Рами Аль Зухури хорош, но я хочу сказать, что может возникнуть проблема с выпуском делегатов из массива, потому что классы NSArray (например, NSMutableArray) сохраняют все добавленные объекты, но делегировать в большинстве случаев свойство присваивания без keepCount. Сохранение делегата может привести к последствиям того, что класс с реализацией делегата будет иметь keepCount + 1. Решение этого - делегаты магазина в NSMutableArray, как указатели, для делегирования методов. Я использую класс singleletone с заголовком делегата.

//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

Ответ 4

Если вы пишете функцию, которая будет вызывать делегатов, вы можете иметь столько, сколько хотите. Но если вы используете класс (который вы не можете изменить), который вызывает делегатов, то вы не можете иметь больше делегатов, чем поддерживает класс.

Вы могли бы, если бы это сработало для вас, попросить одного делегата позвонить другому. Настройте первый делегат, чтобы он вызывал второго делегата (указатель которого хранится в первом объекте делегата). Это может быть простым, поскольку он заранее определен, какие вызовы "переданы" или достаточно сложны, используя механизмы динамического вызова Objective-C.

Ответ 5

Робби Хансон написал многоадресную реализацию делегата. Похоже, что вам нужно. Он рассказывает об этом более подробно здесь и о том, как он используется в XMPPFramework. У него есть хорошая дискуссия об одной из основных проблем, которая заключается в том, как обрабатывать случай, когда несколько делегатов реализуют данный метод, возвращающий значение, определяет поведение класса (а несколько делегатов возвращают разные значения). Соответствующие биты:

Что такое MulticastDelegate?

Структура xmpp должна поддерживать неограниченное количество расширений. Это включает официальные расширения, которые поставляются с каркасом, так как а также любое количество расширений или пользовательский код, который вы, возможно, захотите подключить в рамки. Таким образом, традиционный шаблон делегирования просто не будет Работа. Модули XMPP и расширения должны быть разделены на собственные отдельные классы, но каждый из этих классов должен получить делегат методы. И стандартная архитектура NSNotification не будет работать либо потому, что некоторые из этих делегатов требуют возвратной переменной. (Плюс это действительно раздражает, чтобы извлечь параметры из уведомления словарь userInfo.)

Таким образом, MulticastDelegate позволяет подключаться к инфраструктуре с помощью стандартная парадигма делегата, но она позволяет нескольким классам получать те же уведомления делегатов. Красота в том, что вы не необходимо поместить весь ваш код обработки xmpp в один класс. Ты можешь отделите свою обработку на несколько классов или, как вы считаете нужным.

Ответ 6

Если вы хотите вызывать обратные вызовы для классов B и C из класса A только с одним делегатом, вы можете создать оболочку делегата DWrap, которая имеет ссылки на классы B и C. Затем класс A вызывает обратные вызовы на B и C через DWrap.