1

我遇到了一个UITableView/UICollectionView委托被代理对象(不是一个NSProxy,只是一个常规对象)转发的情况。

根据具体的委托方法,代理会将其转发给实际响应该方法的 2 个对象之一。

给定一个委托回调,我想知道响应方法调用的“真实”实例。

代理代码如下所示:

@implementation DelegateSplitter

- (instancetype)initWithFirstDelegate:(id<NSObject>)firstDelegate secondDelegate:(id<NSObject>)secondDelegate
{
    if(self = [super init])
    {
        _firstDelegate = firstDelegate;
        _secondDelegate = secondDelegate;
    }

    return self;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    SEL aSelector = [anInvocation selector];

    if([self.firstDelegate respondsToSelector:aSelector])
    {
        [anInvocation invokeWithTarget:self.firstDelegate];
    }

    if([self.secondDelegate respondsToSelector:aSelector])
    {
        [anInvocation invokeWithTarget:self.secondDelegate];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *first = [(NSObject *)self.firstDelegate methodSignatureForSelector:aSelector];
    NSMethodSignature *second = [(NSObject *)self.secondDelegate methodSignatureForSelector:aSelector];

    if(first)
    {
        return first;
    }
    else if(second)
    {
        return second;
    }

    return nil;
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if([self.firstDelegate respondsToSelector:aSelector] || [self.secondDelegate respondsToSelector:aSelector])
    {
        return YES;
    }
    else
    {
        return NO;
    }
}

@end

我的代码如下所示:

给定一个委托方法,我想知道哪个实例正在响应:

    // delegate is an instance of DelegateSplitter
    id <UITableViewDelegate> delegate = tv.delegate; 

    SEL didSelectItemSelector = @selector(collectionView:didSelectItemAtIndexPath:);

    if ([delegate respondsToSelector:didSelectItemSelector]) {
       // the delegate splitter doesn't forward the call
       ...
       return;
    }

// the delegate (proxy) forwards the method to a different instance
    if (![delegate.class instancesRespondToSelector:didSelectItemSelector]) {
            // the delegate responds to selector but the class instances themselves do not respond to the selector. This is possible if the delegate is forwarding all invocations to a different object

               NSObject *d = (NSObject *)delegate;
         NSMethodSignature *ms = [d methodSignatureForSelector:didSelectItemSelector];

         if (ms) {

          ** I WANT TO GET THE INSTANCE WHICH IS RESPONDING**
          ????
          HOW DO I GET IT
          ?????
         }
    }

编辑:

当前的 hack 到位(想要更全面的东西):

使转发器在模拟对象上调用并获取目标对象

@implementation NSObject (HACK)
- (id)responderToSelector:(SEL)selector
{
  if ([self respondsToSelector:selector] && [self.class instancesRespondToSelector:selector]) {
    return self; // the class and the instance actually will respond to the selector when called
  }

  if ([self respondsToSelector:selector]) {
    // invocations are forwarded
    id forward = [self forwardingTargetForSelector:selector];
    if (forward && forward != self) {
      return [forward responderToSelector:selector];
    }

      NSMethodSignature *ms = [self methodSignatureForSelector:selector];
      if (ms) {

        MockInvocation *mockInvocation = [MockInvocation invocationWithMethodSignature:ms];
        mockInvocation.selector = selector;
        [self forwardInvocation:mockInvocation];
        return mockInvocation.target ?: mockInvocation.innerTarget;
      }

  }
  return nil;
}


@interface MockInvocation : NSInvocation

@property (nonatomic, weak) id innerTarget;
@end

@implementation IIOMockInvocation

- (void)invoke
{
}
- (void)invokeWithTarget:(id)target
{
  _innerTarget = target;
}

@end
4

0 回答 0