Как да замените набор от делегирани методи с блокове

Имам обект, който дефинира набор от протоколи и делегирани методи

Друг(и) обект(и) отговаря(т) на тях по обичайния шаблон на делегат

   @interface object
   @property (nonatomic,weak) id <myProtocol> delegate;
   @end

object.delegate = someObject;

@interface someObject <object delegate>
// some object delegate methods and their implementation
@end

В много случаи - бих искал да променя поведението на определен метод на делегиране (реализацията на протокола)

В базирания на блок синтаксис бих могъл лесно да присвоя нов "блок" въз основа на моите съображения към делегиращия обект.

Но в стандартния модел на делегат това не е възможно. Един от начините за решаване на проблема би бил да се създаде голяма таблица за изпращане (изявление "if" или "switch" вътре в отговарящия делегат) - но това би било неудобно и би направило много трудно разбирането на кода.

би било много по-лесно да напиша нещо подобно

//standard case
theObject.delegate.blockForMethodOne = ^{the usual code to run} // perhaps how to update a UITableView

if (some condition happened) //something was selected something for instance
{
   theObject.delegate.blockForMethodOne = ^ { some code to run in that case }
}

Без този тип синтаксис ще трябва да напишем нещо подобно

-(void)methodOne
{
  if (standard case)
  {
    //standard code
  }
  else if (self.someConditionHappended) // awkward variable in the object to track changes
  {
     // the code in this case
  }
  // and so on
}

Виждал съм отговори, но те не са достатъчно добри .

Нещо, което може динамично да генерира селектор и блок, би било много по-добро (прокси делегат)

Някаква идея как да се създаде това?

Редактиране:

Въз основа на някои блогове създадох този примерен клас, който ще отговори на всички делегирани методи и ще ги препрати

@interface DelegateManager : NSObject

@property (nonatomic,weak) id proxiedObject;
@property (nonatomic) BOOL justResponded;
@property (nonatomic) BOOL logOnNoResponse;

-(id)init;
-(void)forwardInvocation:(NSInvocation*)invocation;
-(id)proxiedObject;
-(void)setProxiedObject:(id)proxied;
-(BOOL)justResponded;
-(void)setLogOnNoResponse:(BOOL)log;
-(BOOL)logOnNoResponse;



@end

@interface NSMethodSignature (objctypes)
+(NSMethodSignature*)signatureWithObjCTypes:(const char*)types;
@end

@implementation DelegateManager
-(id)init
{
    self = [super init];
    if (self)
    {
        self.proxiedObject   = nil;
        self.justResponded   = NO;
        self.logOnNoResponse = NO;
    }

    return self;
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature *sig;
    sig=[[self.proxiedObject class] instanceMethodSignatureForSelector:selector];
    if(sig==nil)
    {
        // sig=[NSMethodSignature signatureWithObjCTypes:"@^v^c"];
        sig = [[NSObject class] instanceMethodSignatureForSelector: @selector(init)];
    }
    self.justResponded=NO;
    return sig;
}

-(void)forwardInvocation:(NSInvocation*)invocation
{
    if(self.proxiedObject==nil)
    {
        if(self.logOnNoResponse)
            NSLog(@"Warning: proxiedObject is nil! This is a debugging message!");
        return;
    }
    if([self.proxiedObject respondsToSelector:[invocation selector]])
    {
        [invocation invokeWithTarget:self.proxiedObject];
        self.justResponded=YES;
    }
    else if(self.logOnNoResponse)
    {
        NSLog(@"Object \"%@\" failed to respond to delegate message \"%@\"! This is a debugging message.",[[self proxiedObject] class],NSStringFromSelector([invocation selector]));
    }
    return;
}


@end

Този код работи по следния начин

myobject.delegate = self.delegateManager; // always sets itself to this internal "proxy" for the delegate

-(void)setDelegate(id<myProtocol>)delegate
{
  [self.delegateManager setProxiedObject:delegate];
}

съобщенията винаги се изпращат до проксито, което ще се опита да ги предаде напред

myObject:

[self.delegateManager callADelegateMethod:self];

delegateManager е отговорен за предаването на съобщението напред

изглежда, че това може да се разшири, като се направи нещо подобно

if (something happened in my object)
{
// replace the implementation of the selector my delegate supplies
  [self.myObject.delegateManager setBlock:someBlock forSelector:the selector of the delegate];
}

в delegateManager

-(void)setBlock:(someBlock)block forSelector:(selector)aSelector
{
   [self.dictionary setObject:block forKey:aSelector];
}

-(void)forwardInvocation:(NSInvocation*)invocation
{
  //check if there is an entrance of a block for the particular invocation
}

Въпросът е как да създадем ключ, който да е добър за това търсене?


person Avner Barr    schedule 12.11.2013    source източник


Отговори (2)


Ако искате да направите нещо по този начин:

theObject.delegate.blockForMethodOne = ^{the usual code to run}
if (some condition happened)
{
   theObject.delegate.blockForMethodOne = ^ { some code to run in that case }
}

Има няколко въпроса: ще зададете ли делегиран блок (blockForMethodOne) извън делегата? така че откъде ще бъде извикан (от вътрешността на делегата?)? при какво условие ще бъде наречено? Ако разбирам правилно, искате да зададете различно поведение за един и същ блок според някои условия, но за какво? можете просто да извиквате различни методи при различни условия.

Но ако знаете какво правите)) - трябва да създадете клас (например Delegate), който ще съхранява някакъв блок, като този:

typedef void (^BlockCallback)(void);

@interface Delegate : NSObject
@property (nonatomic, copy) BlockCallback blockForMethodOne;
@end

той ще съхранява в blockForMethodOne всичко, което зададете там, но това вече не е модел на делегиране. Задаването на блок на някъде (theObject.delegate.blockForMethodOne = ^{обичайният код за изпълнение}) няма да го изпълни, ако не предефинирате сетер за 'blockForMethodOne', той работи точно като обикновено свойство (просто го запазете), така че имате добавете малко логика, за да го наречете по някакъв начин, на този етап не видях никакви ползи от вашия подход с блокове. Също така можете да поставите @property (nonatomic, copy) BlockCallback blockForMethodOne; в декларацията на протокола, така че всеки клас, който приема този протокол, трябва да има „blockForMethodOne“, ако искате)

Надеждата помага

person in.disee    schedule 12.11.2013

Мисля, че подходящото решение би било да се отървете от члена на делегата и предишния протокол и да преминете към чисто блоково базиран API.

Да кажем, че вашият протокол за делегат беше:

@protocol FooDelegateProtocol <NSObject>
- (void) foo:(Foo*)foo didReceiveData:(NSData*)data;
@end

можете да предефинирате протокол, както следва

typedef void (^foo_receivedDataBlock)(NSData*);

@protocol FooProtocol <NSObject>
@property (nonatomic, copy) foo_receivedDataBlock receivedDataHandler;
@end

Забележете, че това НЕ е протокол за делегата - но е протокол за самия клас Foo, който трябва да го реализира.

Сега вместо делегат имате куп свойства, които са блокове. Вместо да задавате делегата на обект от клас Foo, сега настройвате всеки от блоковете. За всеки делегиран метод вече има съответен блок.

Сега сайтът за обаждания отговаря за настройката на блоковете.

person CouchDeveloper    schedule 12.11.2013