Colas
Colas

Reputation: 3573

Objective-C: Forward most messages to another object (at runtime)

Subclass of UIView

I have a subclass MyView of UIView.

This subclass has a @property UIView * realView.

What I want to do

Whenever a message is sent to MyView, I want to "forward it" to self.realView, excepted for few messages.

For instance, in the implementation of MyView, I would have this override:

- (void)setBackgroundColor:(UIColor *)color
{
     [self.realView setBackgroundColor:color] ;
}

Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?

Exceptions

For some methods, I want to have an explicit control. For instance:

- (void)setFrame:(CGRect)frame
{
    /* do stuff */
    
    [super setFrame:frame] ;
}

Upvotes: 3

Views: 1115

Answers (2)

Hai Feng Kao
Hai Feng Kao

Reputation: 5298

It's entirely possible. First you need WZProtocolIntercepter. Then use the intercepter as the normal UIView:

WZProtocolInterceptor* fakeView = [[WZProtocolInterceptor alloc]
                                  initWithInterceptedProtocol:@protocol(TheMethodsForTheMiddleManToHandle)];

fakeView.receiver = self.realView;
fakeView.middleMan = self;
[someViewController.view addSubview:fakeView];

Put the methods you want to control in TheMethodsForTheMiddleManToHandle:

@protocol TheMethodsForTheMiddleManToHandle
- (void)setFrame:(CGRect)frame;
@end

Upvotes: 0

Caleb
Caleb

Reputation: 125007

Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?

You implement the -forwardInvocation: method to send any unrecognized messages to the other object. -forwardInvocation is called whenever an object doesn't implement the selector that's passed to it as a sort of second chance to handle a message. You can override it to send the message to another object (which is pretty much what NSProxy does), log the messages, etc.

As @cobbal points out below, -forwardInvocation will help you with methods not implemented in your superview, but it won't handle methods that are implemented int the superview because your MyView inherits implementations of those. For example, if you want to use a UIView as a proxy for a UIButton, all the methods specific to UIButton can be handled by -forwardInvocation:, but those defined by UIView cannot. In order to get a behavior other than the inherited method, you will of course need to override. In some situations you can get around that by deriving MyView from NSObject or UIResponder instead of from UIView, thus avoiding the inherited UIView implementations, but if MyView needs to be a real view you're stuck with overriding each method.

If you think about it, it's hard to imagine how your goal could be met without explicitly overriding each inherited method. You say that you only want to forward most messages, but how can the poor runtime tell which ones you do want to forward and which ones you don't? All it can do is look for a method for the given selector and call it if it finds one, or take some action (like calling -forwardInvocation:) if it doesn't.

Update: @robmayoff points out -forwardingTargetForSelector:, which didn't occur to me but is probably a better solution in your case. It still doesn't handle the situation where you need to redirect methods that you inherit from a superclass, though.

Upvotes: 4

Related Questions