如果您在编译时知道选择器及其参数,为什么要在运行时使用 PerformSelector:withObject:withObject 呢?
我刚刚在 Three20 中遇到了一些代码,如下所示:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
}
在 LLVM 2.0 上,这会导致编译错误:
错误:对接口“id”的指针进行算术运算,该指针在非脆弱 ABI 中不是常量大小
我知道为什么会发生该错误,并且我知道如何修复它。我只需要直接调用该方法,如下所示:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
}
我的问题是,如果您在编译时知道选择器及其参数,为什么需要在运行时使用 performSelector:withObject:withObject:
?我不明白为什么代码首先要这样写。如果选择器和参数是动态传递到方法中的,我可能会理解,但事实并非如此,选择器及其参数是硬编码的,(即使索引在运行时确实发生变化,其获取索引的方法也是硬编码的)编码。)
如果有人可以向我解释为什么这是必要的,我将不胜感激。否则,我将在这里更改所有这些代码。
I've just come across some code in Three20 that looks like this:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
}
On LLVM 2.0, this causes the compilation error:
error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI
I know why that error is occurring and I know how to fix it. I just need to invoke the method directly, like so:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
}
My question is, if you know both the selector and its arguments at compile time, why would you need to use performSelector:withObject:withObject:
at runtime? I don't see why the code was written this way in the first place. If the selector and arguments were dynamically passed into the method, I may understand, but they're not, the selector and its arguments are hard coded, (even if the index does change during run time, its method of obtaining the index is hard coded.)
If someone could explain to me a good reason why this would be necessary, I'd be grateful. Otherwise, I'll be over here changing all this code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
经过进一步挖掘,发现此代码所在的 TTPickerTextField 类似乎是 UITextField 的间接子类。
因此,它依赖于
UITextField
的委托属性,该属性不符合TTPickerTextFieldDelegate
协议,其中方法textField:didAddCellAtIndex:
已声明。我得出的结论是这段代码只是懒惰。没有理由必须搭载
UITextField
的委托属性,从而使这种令人困惑、容易出错的代码变得必要。我自己的方法是单独保留 UITextField 的委托属性,并在处理特定委托方法的特定子类中添加我自己的属性。
只是为了澄清 - 我在问题中提到的“解决方案”修复了编译器错误,但会生成一条警告,指出无法找到该方法并且将假定返回 id。这就是原始代码“解决”的问题,但仅在 GCC 中有效。 LLVM 2.0 不再适用。
最后一次编辑,我保证:
我对抗这种懒惰并摆脱警告和错误的最终解决方案是一个丑陋的黑客:
将
UITextField
s 委托转换为符合以下条件的id
TTPickerTextFieldDelegate
然后直接调用该方法。请不要偷懒:(
After a little more digging, it looks like the
TTPickerTextField
class that this code is found in is an indirect subclass of aUITextField
.As such, it is piggy-backing on
UITextField
s delegate property, which doesn't conform to theTTPickerTextFieldDelegate
protocol where the methodtextField:didAddCellAtIndex:
is declared.I have come to the conclusion that this code is just laziness. No reason why the
UITextField
s delegate property had to be piggy-backed, making this confusing, error prone code necessary.My own approach would have been to leave
UITextField
s delegate property alone, and add my own property in my specific subclass that handled the specific delegate methods.Just to clarify - the 'solution' I mentioned in the question fixes the compiler error, but generates a warning that the method can't be found and will be assumed to return id. This is what the original code was 'solving' but that only worked in GCC. No longer with LLVM 2.0.
Last edit, I promise:
My final solution to combat this laziness and get rid of the warning and error is an ugly hack:
Cast
UITextField
s delegate to anid
that conforms toTTPickerTextFieldDelegate
and then invoke the method directly.Please don't be lazy :(
respondsToSelector/performSelector 组合是可选委托方法的惯用语。委托不能保证定义该方法,因此直接调用它会导致编译器警告。
在这种情况下,编译器实际上抱怨的是:
有风险的指针算术...“id”是指针类型,因此:
告诉编译器它将从指针而不是整数中减一...这可能是不是该代码的意图。 PerformSelector 的 withObject 参数必须是一个指针,它不能是一个原语。您可以通过将 _cellViews.count - 1 包装在 NSNumber 中并在委托方法中展开来解决此问题。
That respondsToSelector/performSelector combo is an idiom for optional delegate methods. The delegate isn't guaranteed to have that method defined, so a direct call to it would cause a compiler warning.
What the compiler was actually complaining about in this case:
is risky pointer arithmetic... 'id' is a pointer type, so:
tells the compiler it's going to subtract one from a pointer instead of an integer....which is probably not the intent of that code. The withObject argument of performSelector has to be a pointer, it can't be a primitive. You can get around this by wrapping _cellViews.count - 1 in an NSNumber, and unwrapping it in the delegate method.