Зачем использовать executeSelector: withObject: withObject во время выполнения, если вы знаете и селектор, и его аргументы во время компиляции? - PullRequest
6 голосов
/ 05 февраля 2011

Я только что натолкнулся на некоторый код в 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: во время выполнения?Я не понимаю, почему код был написан именно так.Если селектор и аргументы были динамически переданы в метод, я могу понять, но это не так, селектор и его аргументы жестко закодированы (даже если индекс изменяется во время выполнения, его метод получения индекса сложен).закодировано.)

Если бы кто-то мог объяснить мне вескую причину, почему это было бы необходимо, я был бы благодарен.В противном случае я буду здесь, чтобы изменить весь этот код.

Ответы [ 2 ]

10 голосов
/ 05 февраля 2011

После еще нескольких копаний похоже, что класс TTPickerTextField, в котором находится этот код, является косвенным подклассом UITextField.

. Таким образом, он поддерживает P * 1005.* Свойство делегата, которое не соответствует протоколу TTPickerTextFieldDelegate, где объявлен метод textField:didAddCellAtIndex:.

Я пришел к выводу, что этот код - просто лень.Нет причин, по которым свойство делегата UITextField должно было бы иметь поддержку, что делает этот запутанный, подверженный ошибкам код необходимым.

Мой собственный подход состоял бы в том, чтобы оставить свойство делегата UITextField в покое и добавитьмое собственное свойство в моем конкретном подклассе, который обрабатывал определенные методы делегата.

Просто чтобы уточнить - «решение», о котором я говорил в этом вопросе, исправляет ошибку компилятора, но генерирует предупреждение о том, что метод не может быть найдени предполагается, что вернуть идентификатор.Это то, что исходный код «решал», но это работало только в GCC.Больше не с LLVM 2.0.

Последнее редактирование, я обещаю:

Мое окончательное решение для борьбы с этой ленью и избавления от предупреждений и ошибок - отвратительный хак:

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

Приведите UITextField s делегата к id, который соответствует TTPickerTextFieldDelegate, и затем вызовите метод напрямую.

Пожалуйста, не ленитесь: (

5 голосов
/ 05 февраля 2011

То, что отклик комбинируется с ToSelector / executeSelector, является идиомой для необязательных методов делегата.Не гарантируется, что делегат определит этот метод, поэтому прямой вызов его вызовет предупреждение компилятора.

На что фактически жаловался компилятор в этом случае:

[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

сообщает компилятору, что он собирается вычесть единицу из указателя вместо целого числа ... что, вероятно, не является целью этого кода.Аргумент withObject для executeSelector должен быть указателем, он не может быть примитивом.Вы можете обойти это, обернув _cellViews.count - 1 в NSNumber, и развернув его в методе делегата.

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