如果您在编译时知道选择器及其参数,为什么要在运行时使用 PerformSelector:withObject:withObject 呢?

发布于 2024-10-16 06:04:48 字数 869 浏览 1 评论 0原文

我刚刚在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

昔梦 2024-10-23 06:04:48

经过进一步挖掘,发现此代码所在的 TTPickerTextField 类似乎是 UITextField 的间接子类。

因此,它依赖于 UITextField 的委托属性,该属性不符合 TTPickerTextFieldDelegate 协议,其中方法 textField:didAddCellAtIndex: 已声明。

我得出的结论是这段代码只是懒惰。没有理由必须搭载 UITextField 的委托属性,从而使这种令人困惑、容易出错的代码变得必要。

我自己的方法是单独保留 UITextField 的委托属性,并在处理特定委托方法的特定子类中添加我自己的属性。

只是为了澄清 - 我在问题中提到的“解决方案”修复了编译器错误,但会生成一条警告,指出无法找到该方法并且将假定返回 id。这就是原始代码“解决”的问题,但仅在 GCC 中有效。 LLVM 2.0 不再适用。

最后一次编辑,我保证:

我对抗这种懒惰并摆脱警告和错误的最终解决方案是一个丑陋的黑客:

[(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];

UITextFields 委托转换为符合以下条件的 id TTPickerTextFieldDelegate 然后直接调用该方法。

请不要偷懒:(

After a little more digging, it looks like the TTPickerTextField class that this code is found in is an indirect subclass of a UITextField.

As such, it is piggy-backing on UITextFields delegate property, which doesn't conform to the TTPickerTextFieldDelegate protocol where the method textField:didAddCellAtIndex: is declared.

I have come to the conclusion that this code is just laziness. No reason why the UITextFields delegate property had to be piggy-backed, making this confusing, error prone code necessary.

My own approach would have been to leave UITextFields 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:

[(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];

Cast UITextFields delegate to an id that conforms to TTPickerTextFieldDelegate and then invoke the method directly.

Please don't be lazy :(

你的背包 2024-10-23 06:04:48

respondsToSelector/performSelector 组合是可选委托方法的惯用语。委托不能保证定义该方法,因此直接调用它会导致编译器警告。

在这种情况下,编译器实际上抱怨的是:

[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];

error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI

有风险的指针算术...“id”是指针类型,因此:

(id)_cellViews.count-1

告诉编译器它将从指针而不是整数中减一...这可能是不是该代码的意图。 PerformSelector 的 withObject 参数必须是一个指针,它不能是一个原语。您可以通过将 _cellViews.count - 1 包装在 NSNumber 中并在委托方法中展开来解决此问题。

[self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]];

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:

[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];

error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI

is risky pointer arithmetic... 'id' is a pointer type, so:

(id)_cellViews.count-1

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.

[self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文