У меня есть два класса, которые могут выступать в качестве делегата третьего класса, и оба реализуют формальный протокол, сделанный полностью из необязательных методов. Один из классов реализует все, в то время как другой только реализует пару методов, о которых я забочусь. Тем не менее, во время выполнения, когда у меня есть второй класс, выступающий в качестве делегата третьего класса, а третий класс вызывает вызов одного из нереализованных необязательных методов для этого делегата, я получаю ошибку времени выполнения, в основном говоря: "Target не отвечает на это селектор сообщений". Я думал, что objective-c правильно обработал этот случай и что он просто ничего не сделает, если этот метод фактически не определен в классе. Может быть, что-то мне не хватает?
Почему нереализованные необязательные методы протокола вызывают ошибки во время выполнения, когда этот метод вызывается в obj-c?
Ответ 1
Когда вы вызываете необязательный метод вашего делегата, вам нужно убедиться, что он отвечает на селектор, прежде чем вызывать его:
if ([delegate respondsToSelector:@selector(optionalMethod)])
[delegate optionalMethod];
Ответ 2
Необязательные методы протокола просто означают, что объект, реализующий протокол, не должен реализовывать данный метод - тогда вызывающий должен обязательно проверить, реализует ли объект метод перед вызовом (в противном случае вы столкнетесь, как вы заметили). Эти категории NSObject 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)];
Ответ 3
Это решение блоков работает хорошо, как только вы получаете свою голову, обернутую вокруг того, что происходит. Я добавил результат BOOL, потому что хотел иметь возможность условно запустить один из нескольких необязательных методов. Некоторые советы, если вы пытаетесь реализовать это решение:
Во-первых, если вы еще не сталкивались с расширением/Категории, вы просто добавляете это в начало своего класса, ВНЕШНЕЕ определение существующего класса. Это будет публичное или частное расширение, основанное на том, где вы его разместили.
@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:)];
Ответ 4
Блоки могут обеспечить лучшее решение. Они позволяют условно выполнять любой код, основанный на существовании реализации данного метода:
-(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{
if ([self respondsToSelector:aSelector]) {
block();
}
}
Используя это дополнение к NSObject, вы можете условно выполнить любой метод @optional, независимо от того, сколько параметров оно может иметь.
См. Как безопасно отправлять сообщения @optional протокола, которые могут быть не реализованы