#ios #objective-c #objective-c-runtime
#iOS #objective-c #objective-c-runtime
Вопрос:
Подкласс UIView
У меня есть подкласс MyView
UIView
.
Этот подкласс имеет @property UIView * realView
.
Что я хочу сделать
Всякий раз, когда отправляется сообщение MyView
, я хочу «переслать его» self.realView
, за исключением нескольких сообщений.
Например, в реализации MyView
я бы переопределил это:
- (void)setBackgroundColor:(UIColor *)color
{
[self.realView setBackgroundColor:color] ;
}
Могу ли я сделать это автоматически, во время выполнения, вместо явного переопределения всех методов?
Исключения
Для некоторых методов я хочу иметь явный контроль. Например:
- (void)setFrame:(CGRect)frame
{
/* do stuff */
[super setFrame:frame] ;
}
Комментарии:
1. Проверьте это! mikeash.com/pyblog /…
2. Это может помочь узнать, почему вы хотите, чтобы один экземпляр view пересылал сообщения другому, а не обрабатывал сами сообщения. Это кажется очень необычным. Кроме того, какие критерии вы используете, чтобы определить, какие сообщения пересылать?
Ответ №1:
Могу ли я сделать это автоматически, во время выполнения, вместо явного переопределения всех методов?
Вы реализуете -forwardInvocation:
метод для отправки любых нераспознанных сообщений другому объекту. -forwardInvocation
вызывается всякий раз, когда объект не реализует селектор, который передается ему как своего рода второй шанс обработать сообщение. Вы можете переопределить его, чтобы отправить сообщение другому объекту (что в значительной степени делает NSProxy), регистрировать сообщения и т. Д.
Как указывает @cobbal ниже, -forwardInvocation
поможет вам с методами, не реализованными в вашем супервизоре, но он не будет обрабатывать методы, которые реализованы в супервизоре, потому что вы MyView
наследуете их реализации. Например, если вы хотите использовать a UIView
в качестве прокси для a UIButton
, все методы, специфичные для UIButton
, могут обрабатываться -forwardInvocation:
, но те, которые определены UIView
, не могут. Чтобы получить поведение, отличное от унаследованного метода, вам, конечно, потребуется переопределить. В некоторых ситуациях вы можете обойти это, извлекая MyView
из NSObject
или UIResponder
вместо из UIView
, избегая таким образом унаследованных UIView
реализаций, но если MyView
требуется реальное представление, вы застряли с переопределением каждого метода.
Если вы подумаете об этом, трудно представить, как ваша цель может быть достигнута без явного переопределения каждого унаследованного метода. Вы говорите, что хотите пересылать только большинство сообщений, но как плохая среда выполнения может определить, какие из них вы хотите пересылать, а какие нет? Все, что он может сделать, это найти метод для данного селектора и вызвать его, если он его найдет, или выполнить какое-либо действие (например, вызов -forwardInvocation:
), если это не так.
Обновление: @robmayoff указывает -forwardingTargetForSelector:
, что мне это не приходило в голову, но, вероятно, в вашем случае это лучшее решение. Однако он по-прежнему не справляется с ситуацией, когда вам нужно перенаправлять методы, которые вы наследуете от суперкласса.
Комментарии:
1. Это не будет работать для стандартных методов просмотра, поскольку суперкласс (UIView) будет обрабатывать их и
-forwardInvocation:
никогда не будет вызываться2. @cobbal Это правда — оператору придется переопределять методы, определенные в суперклассе. Я обновлю, чтобы упомянуть об этом.
3. поскольку оба класса являются UIViews, а подкласс отвечает первым, все методы необходимо вызывать вручную.
4. Также обратите внимание, что
forwardingTargetForSelector:
это намного проще реализовать и намного эффективнее во время выполнения.5. Может быть, я мог бы написать макрос, который принимает строку типа
"setSubviews:"
и который автоматически записываетforwardingTarget...
Ответ №2:
Это вполне возможно. Сначала вам нужен WZProtocolIntercepter. Затем используйте перехватчик в качестве обычного пользовательского интерфейса:
WZProtocolInterceptor* fakeView = [[WZProtocolInterceptor alloc]
initWithInterceptedProtocol:@protocol(TheMethodsForTheMiddleManToHandle)];
fakeView.receiver = self.realView;
fakeView.middleMan = self;
[someViewController.view addSubview:fakeView];
Поместите методы, которыми вы хотите управлять, в TheMethodsForTheMiddleManToHandle
:
@protocol TheMethodsForTheMiddleManToHandle
- (void)setFrame:(CGRect)frame;
@end