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
?????
}
}
編集:
現在のハック (より全体的なものが欲しい):
モック オブジェクトでフォワーダーを呼び出し、ターゲット オブジェクトを取得する
@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