Почему нереализованные необязательные методы протокола вызывают ошибки времени выполнения, когда этот метод вызывается в obj-c? - PullRequest
10 голосов
/ 22 июня 2009

У меня есть два класса, которые могут выступать в качестве делегата третьего класса, и оба реализуют формальный протокол, полностью состоящий из необязательных методов. Один из классов реализует все, в то время как другой реализует только пару методов, которые меня волнуют. Однако во время выполнения, когда у меня есть второй класс, выступающий в качестве делегата в третьем классе, и третий класс заканчивает тем, что вызывает один из невыполненных необязательных методов для этого делегата, я получаю ошибку времени выполнения, по существу говоря, селектор сообщений. " Я думал, что target-c правильно обработал этот случай и что он ничего не сделает, если этот метод не будет определен в классе. Могу ли я что-то пропустить?

Ответы [ 4 ]

33 голосов
/ 22 июня 2009

Когда вы вызываете дополнительный метод вашего делегата, вы должны убедиться, что он отвечает селектору, прежде чем вызывать его:

if ([delegate respondsToSelector:@selector(optionalMethod)])
    [delegate optionalMethod];
10 голосов
/ 23 июня 2009

Необязательные методы протокола просто означают, что объект, реализующий протокол, не должен реализовывать рассматриваемый метод - тогда вызываемый объект обязательно должен проверить, реализует ли объект метод перед вызовом (в противном случае, как вы заметили, произойдет сбой). Эти категории HSObject HOM могут быть полезны:

@implementation NSObject (Extensions)

- (id)performSelectorIfResponds:(SEL)aSelector
{
    if ( [self respondsToSelector:aSelector] ) {
        return [self performSelector:aSelector];
    }
    return NULL;
}

- (id)performSelectorIfResponds:(SEL)aSelector withObject:(id)anObject
{
    if ( [self respondsToSelector:aSelector] ) {
        return [self performSelector:aSelector withObject:anObject];
    }
    return NULL;
}

@end

Тогда вы можете просто сделать:

[delegate performSelectorIfResponds:@selector(optionalMethod)];
4 голосов
/ 19 октября 2012

Это решение Blocks работает хорошо, как только вы ловите голову на том, что происходит. Я добавил результат BOOL, потому что хотел иметь возможность условно запустить один из нескольких дополнительных методов. Несколько советов, если вы пытаетесь реализовать это решение:

Во-первых, если вы еще не сталкивались с Extension / Categories, вы просто добавляете это в начало своего класса, ВНЕ существующего определения класса. Это будет публичное или частное расширение в зависимости от того, где вы его разместили.

@implementation NSObject (Extensions)
// add block-based execution of optional protocol messages
-(BOOL) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector
{
    if ([self respondsToSelector:aSelector]) {
        block();
        return YES;
    }
    return NO;
}
@end

Во-вторых, вот как вы это называете из своего кода:

BOOL b = [(NSObject*)self.delegate performBlock:^{
    // code to run if the protocol method is implemented
} 
ifRespondsTo:@selector(Param1:Param2:ParamN:)];

Замените Param1: Param2: ParamN: именами каждого параметра для вашего метода протокола. Каждый должен заканчиваться двоеточием. Так что если ваш метод протокола выглядит так:

-(void)dosomething:(id)blah withObj:(id)blah2 andWithObj(id)blah3;

последняя строка будет выглядеть так:

ifRespondsTo:@selector(dosomething:withObj:andWithObj:)];

1 голос
/ 24 октября 2011

Блоки могут обеспечить лучшее решение. Они позволяют условно выполнить любой код, основанный на существовании реализации данного метода:

-(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{
    if ([self respondsToSelector:aSelector]) {
        block();
    }
}

Используя это дополнение к NSObject, вы можете условно выполнить любой метод @optional, независимо от того, сколько у него параметров.

См. Как безопасно отправлять сообщения протокола @opional, которые могут быть не реализованы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...