2013-08-21 1 views
1

У меня есть контроллер представления, который определяет протокол, который сам наследует другой протокол. Я хочу, чтобы любой объект, который реализует мой протокол, также реализует унаследованный протокол.объектив c пересылка сообщения с переадресациейTargetForSelector не всегда работает

Я хочу, чтобы установить свой класс, чтобы перехватывать некоторые из сообщений в унаследованном протоколе для того, чтобы настроить некоторые вещи внутри, но в конечном счете, хотел бы направить все сообщения на делегат моего класса

Я мог бы написать много кода плиты котла, чтобы заглушить весь протокол, и стажер вызовет делегата, но я вижу, что он ломается много времени - в любое время, когда изменяется «супер» протокол, мне нужно снова создать этот класс.

Я вижу, что это очень преобладает в пользовательских элементах управления пользовательским интерфейсом. При повторном использовании существующих компонентов - например, таблиц или представлений коллекции, вы хотели бы, чтобы ваш источник данных отвечал на все распространенные протоколы, но в некоторых случаях вам нужно настроить представление в соответствии с индексом или сохранить определенное состояние.

Я попытался с помощью forwardingTargetForSelector для того, чтобы пересылать сообщения, которые я не отвечаю, но это не всегда экспедиторское ...

Вот надуманный пример кода:

Class A: (the top most protocol) 

@ 

    protocol classAProtocol <NSObject> 

    -(void)method1; 
    -(void)method2; 
    -(void)method3; 

    @end 

    My Class 

    @protocol MyClassProtocol <classAProtocol> 

    -(void)method4; 
    @end 

    @interface MyClass 

    @property (nonatomic,weak> id <MyClassProtocol> delegate; 

    @end 

    @interface MyClass (privateInterface) 

    @property (nonatomic,strong) ClassA *classAObject; 
    @end 
    @implementation MyClass 

    -(init) 
    { 
     self = [super init]; 
     if (self) 
     { 
     _classAObject = [[ClassA alloc] init]; 
     _classAObject.delegate = self; // want to answer some of the delegate methods but not all 
     } 
    } 

-(void)method1 
{ 
    // do some internal configuration 
    // call my delegate with 
    [self.delegate method1]; 
} 
    -(id)forwardingTargetForSelector:(SEL)aSelector 
    { 
     if ([self respondsToSelector:aSelector]) 
     { 
      return self; 
     } 
     if ([self.delegate respondsToSelector:aSelector]) 
     { 
      return self.delegate; 
     } 
     return nil; 
    } 

    -(void)setDelegate:(id <MyClassProtocol>)delegate 
    { 
     self.delegate = delegate; // will forward some of the messages 
    } 
    @end 
+0

Попробую свои решения и вернуться к вам. –

ответ

0

Вы никогда не должны return self; от forwardingTargetForSelector:. Ваш чек должен означать, что это никогда не произойдет, но если вы когда-либо вернетесь self, это вызовет бесконечный цикл.

Вы должны быть уверены, что суперкласс не реализует метод, так как это предотвратит вызов forwardingTargetForSelector:. Проверьте, действительно ли метод вызван.

forwardingTargetForSelector: также вызывается только при вызове метода на контроллере, на который он не отвечает. В вашем примере вы не звоните [self ...];, вы звоните [self.delegate ...];, поэтому forwardingTargetForSelector: не будет вызываться.

+0

Непонятно, о чем вы говорите. Если я скажу в своем классе self.someObject.delegate = self; И не реализовать его делегат, не будет ли сообщение отправлено моему делегату, который, как ожидается, его реализует? –

+0

Методы не будут перенаправлены на другой объект, если вы не напишете какой-либо код, чтобы это произошло. Если вы вызываете 'self' и не реализуете метод (и суперкласс не реализует его), ваш' forwardingTargetForSelector' отправит его. – Wain

2

Возвращаясь от forwardingTargetForSelector:, не имеет смысла, потому что он никогда не будет вызван, если бы сам ответил на селектор.

Вы должны реализовать эти три метода:

- (void)forwardInvocation:(NSInvocation *)anInvocation 
{ 
    if ([self.delegate respondsToSelector:[anInvocation selector]]) 
     [anInvocation invokeWithTarget:self.delegate]; 
    else 
     [super forwardInvocation:anInvocation]; 
} 

- (BOOL)respondsToSelector:(SEL)aSelector 
{ 
    return [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector]; 
} 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 
{ 
    NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 
    if (!signature) { 
     signature = [self.delegate methodSignatureForSelector:selector]; 
    } 
    return signature; 
} 
+0

Добавлены эти реализации. Не знаете, что делает третий метод (methodSignatureForSelector). Селектор должен быть тем же самым в делегате, не так ли? Также если делегат не реализует этот метод, вызывающий [self.delegate methodSignatureForSelector: selector]; не компилируется –

+0

Используется системой при построении NSInvocation. Это объяснит все, что было за кодом, который я предоставил: https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html – atomkirk