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: немного.

Другие вопросы по тегам