Objective-C - есть ли способ использовать какой-либо класс (UIView) в качестве протокола для другого объекта?
Я экспериментировал в Objective-C с некоторыми довольно специфическими концепциями.
Ниже приведен пример: по сути, у меня есть объект с именем "theFilter", который должен получить все сообщения плюс "Some_More_Messages" от другого объекта "theSender". Экземпляры фильтра предназначены для воспроизведения фильтра на любом количестве экземпляров, всех разных классов. Основываясь на некоторых критериях, он отправляет сообщения в протоколе "Some_More_Messages" другому объекту, тогда как все остальные просто выбирают стандартный путь.
Filter<Some_More_Messages> *theFilter;
Например, я хочу, чтобы этот экземпляр фильтра был перед экземпляром UIView, и мне нужен мой объект "Filter", чтобы иметь возможность получать все сообщения, которые реализует UIView. Что-то вроде:
Filter<UIView,Some_More_Messages> *theFilter;
Но я не могу продолжать и создавать протоколы для каждого класса, для которого мне нужен фильтр. Это сделано для того, чтобы компилятор прекратил жаловаться и получил автозаполнение и все остальное.
Есть идеи?
PS. Внутри кода для "theSender" я никогда не обращаюсь непосредственно к экземпляру UIView, а вместо этого говорю с его "filterInstance" и отправляю ему "Some_More_Messages", чем знает экземпляр UIView. Все, что он знает, выполняется, и все остальные селекторы перенаправляются в другой экземпляр.
2 ответа
Вы можете реализовать forwardingTargetForSelector:
@interface Filter : NSObject /*NSProxy*/ <FilterProtocol>
@property UIView *view;
+ (UIView<FilterProtocol> *)filterWithView:(UIView *)view;
@end
@implementation
+ (UIView<FilterProtocol> *)filterWithView:(UIView *)view {
return (UIView<FilterProtocol> *)[[self alloc] initWithView:view];
}
- (id)initWithView:(UIView *)view {
self = [super init];
if (self) {
self.view = view;
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.view respondsToSelector:aSelector]) {
return self.view;
}
return [super forwardingTargetForSelector:aSelector];
}
- (void)someFilterProtocolMethod {}
@end
UIView<FilterProtocol> *filteredView = [Filter filterWithView:view];
[filteredView addSubview:otherView]; // call addSubview on view
[filteredView someFilterProtocolMethod]; // call someFilterProtocolMethod on filter object
Обновление: это хорошая идея, но плохая реализация, проверьте ответ Брайана Ченга для правильной реализации.
Я думаю, что вы пытаетесь добиться множественного наследования. Хотя Objective-C не поддерживает это, вы можете имитировать его, имея экземпляр UIView
и пересылать необработанные сообщения на него.
Вы должны направить NSInvocations на UIView
, как это:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self respondsToSelector:[anInvocation selector]]) {
// Try to use our version of the selector
[super forwardInvocation:anInvocation];
} else {
// But if we don't have that method, delegate it to the UIView
[anInvocation invokeWithTarget:self.view];
}
}
Вы также можете обмануть другие объекты, переопределив respondsToSelector:
, isKindOfClass:
и некоторые другие методы, чтобы сделать его похожим на UIView. Подробнее об этой продвинутой технике читайте в Руководстве по программированию во время выполнения.
Кстати, если вы просто не хотите, чтобы он падал, но на самом деле не нужно пересылать сообщение на внутренний UIView
затем сделайте respondsToSelector:
метод вернуть ДА, если это UIView
метод, но ничего не делать на forwardInvocation:
немного.