我正在尝试通过设置一个 NSProxy 子类来代替我选择的任何 UIView,向我的 UIViews 添加功能(根据状态配置 CALayer)。这是我尝试过的:
在我的 NSProxy 子类中,我有以下代码:
#pragma mark Initialization / Dealloc
- (id)initWithView:(UIView *)view
{
delegate = view;
[delegate retain];
return self;
}
- (void)dealloc
{
[delegate release];
[super dealloc];
}
#pragma mark Proxy Methods
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:delegate];
[anInvocation invoke];
return;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [delegate methodSignatureForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL rv = NO;
if ([delegate respondsToSelector:aSelector]) { rv = YES; }
return rv;
}
并且,以这种方式使用我的 NSProxy 子类:
UILabel *label = [[HFMultiStateProxy alloc] initWithView:[[[UILabel alloc] initWithFrame:cellFrame] autorelease]];
label.text = text;
label.font = font;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[self addSubview:label];
似乎可以工作,直到我点击 addSubview: 行。
打开消息跟踪( instrumentObjcMessageSends(YES); )显示了之前每条消息的转发,直到深入 addSubview:,这一系列方法调用显示在日志中(此处显示的第一条消息是通过代理人):
- UILabel UIView _makeSubtreePerformSelector:withObject:
- UILabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- NSMethodSignature NSMethodSignature methodReturnType
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
+ UILabel NSObject resolveInstanceMethod:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject class
- UILabel NSObject doesNotRecognizeSelector:
我收到以下错误:
2011-02-20 16:38:52.048 FlashClass_dbg[22035:207] -[UILabel superlayer]: unrecognized selector sent to instance 0x757d470
如果我不使用 NSProxy 子类而是使用 UILabel 子类 (HFMultiStateLabel),它就可以正常工作。这是调用 addSubview: 后发生的消息跟踪(HFNoteNameControl 是标签的 super View ):
- HFNoteNameControl UIView addSubview:
- HFNoteNameControl UIView _addSubview:positioned:relativeTo:
- HFMultiStateLabel UIView superview
- HFMultiStateLabel UIView window
- HFNoteNameControl NSObject isKindOfClass:
- HFNoteNameControl NSObject class
- HFNoteNameControl UIView window
- UIWindow NSObject isKindOfClass:
- UIWindow NSObject class
- HFNoteNameControl UIView _shouldTryPromoteDescendantToFirstResponder
- HFMultiStateLabel UIView _isAncestorOfFirstResponder
- HFMultiStateLabel UIView _willMoveToWindow:withAncestorView:
- HFMultiStateLabel UIView _willMoveToWindow:
- HFMultiStateLabel UIView willMoveToWindow:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- HFMultiStateLabel UIView willMoveToSuperview:
- HFMultiStateLabel UIView _unsubscribeToScrollNotificationsIfNecessary:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- CALayer CALayer superlayer
我可以验证在使用 NSProxy 时成功调用了 -superlayer 之前的每个方法。出于某种原因,使用 NSProxy,UILabel 上的 superlayer 被调用而不是 CALayer。也许某个地方有些困惑,UILabel 被插入到子层而不是它的 CALayer 中?
我错过了什么吗?
UIKit 是否做了某种优化来绕过 NSProxy Hook 的正常机制?
其他想法?
谢谢!
亨利
PS 我只在模拟器中尝试过这个,而不是在设备中。这种行为会有所不同吗?
最佳答案
我试图解决同样的问题 - 当我遇到这个问题时,将 NSProxy 与 UIView(在我的情况下为 UITableViewCell)一起使用。我记录了对控制台的所有调用:
...
App[2857:c07] MyHeaderCell: --- method signature for: _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- method signature for: _makeSubtreePerformSelector:withObject:
App[2857:c07] MyHeaderCell: --- _makeSubtreePerformSelector:withObject:
App[2857:c07] +[MyHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] CRASH: +[SMSHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] Stack Trace:...
它在
unrecognized selector
异常上崩溃。通常,对象首先被询问
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
方法,当它返回时,它调用代理中的 - (void) forwardInvocation:(NSInvocation *)invocation
。这样我们就可以重定向消息。如果没有返回 NSMethodSignature
,则在对象上调用 doesNotRecognizeSelector:
方法。所以我们甚至会收到无法识别的选择器调用。这适用于实例方法,但这种崩溃是由类方法引起的,我们无权控制它 - 对象本身没有被调用(类是)。我想通过覆盖我的 NSProxy 子类的 getter 来强制运行时调用我的代理类,即使是类方法
- (Class) class
{
return _myRealClass;
}
哪个不起作用。所以 NSProxy 不足以做到这一点。现在我正在尝试使用 NSObject 而不是 NSProxy 来实现所有所需的行为,因为 NSObject 具有可能有用的
+ (BOOL)resolveClassMethod:(SEL)sel
方法。一旦我发现 NSObject 是否更适合于此,我将编辑这篇文章。//编辑
似乎问题在于,对于 NSProxy,
superlayer
正在 UIView
而不是 CALayer
上被调用。证明:http://t2523.codeinpro.us/q/508112244f1eba38a4fb032e所以它看起来真的像是一个 UIKit 快捷方式问题——他们没有发送常规的消息调用(我猜是速度优化)。
无论如何,我现在正在寻找一种方法来解决这个问题。
关于iphone - 任何人都成功使用 UIView 的 NSProxy(例如,UILabel?),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5061223/