Как тип "id" понимает получатель метода без приведения?

После слияния master с моей рабочей веткой я получил ошибку компилятора в строке, которая не была изменена. Ошибка выглядит

id test;
[test count];

Найдено несколько методов с именем 'count' с ошибочным результатом.

Сначала это выглядит ясно, потому что компилятор не знает, к какому конкретному типу относится переменная "test". Но я не понимаю, почему это сработало раньше.

  1. Если я создаю новый файл, эта строка работает, предполагая, что это метод NSArray. Почему компилятор не показывает ошибку в этом случае?

  2. При отображении сообщения об ошибке показано несколько возможных приемников метода подсчета. (NSArray, NSDictionary, NSSet) Ищет ли он все классы, которые могут получить это сообщение и показать ошибку, если их несколько?

  3. Я заметил, что ошибка возникает при импорте файла "-Swift.h". Как это зависит?

2 ответа

Решение

Objective-C не должен знать тип получателя. Во время выполнения все объекты просто idи все динамически отправляется. Таким образом, любое сообщение может быть отправлено любому объекту, независимо от его типа. (Во время выполнения объекты могут сами решать, что делать с сообщениями, которые они не понимают. Наиболее распространенная вещь, которую нужно сделать, это вызвать исключение и аварийный отказ, но есть много видов объектов, которые могут обрабатывать произвольные сообщения, которые не ' т сопоставить непосредственно с вызовами методов.)

Однако есть несколько технических деталей, которые усложняют это.

ABI (двоичный интерфейс приложения) определяет различные механизмы для возврата определенных примитивных типов. Пока значение является "целочисленным размером в слово", тогда это не имеет значения (это включает в себя такие вещи, как NSInteger и все указатели, что означает расширение всех объектов). Но на некоторых процессорах числа с плавающей точкой возвращаются в других регистрах, чем целые числа, и структуры (например, CGRect) могут быть возвращены различными способами в зависимости от их размера. Чтобы написать необходимый язык ассемблера, компилятор должен знать, какой будет возвращаемое значение.

ARC добавил дополнительные складки, которые требуют, чтобы компилятор знал больше о типе параметров (особенно, являются ли они объектами или примитивами), и есть ли какие-либо атрибуты управления памятью, которые необходимо учитывать.

Компилятору все равно, какой "настоящий" тип test до тех пор, пока он может выяснить типы и атрибуты -count, Так что когда имеем дело с id значение, он просматривает каждый известный селектор, который он может видеть (то есть каждый, определенный во включенном заголовке или текущем .m). Хорошо, если их много в разных классах, если они все согласны. Но если он вообще не может найти селектор или некоторые интерфейсы не согласны, он не может скомпилировать строку кода.

Как отмечает lobstah, у вас, вероятно, есть где-то в вашем коде Swift, который имеет @objc метод называется count() или @objc свойство по имени count который возвращает что-то другое, чем Int (который отображается на NSIntegerи так совпадают с обычной подписью -count). Вам нужно исправить этот метод или скрыть его от ObjC (например, добавив @nonobjc).

Или намного лучше: избавиться от idи использовать его фактический тип. id Обычно это плохая идея в Какао, и особенно плохая, если вы вызываете методы для нее, так как компилятор не может проверить, будет ли объект реагировать, и вы можете потерпеть крах.

Компилятор не приводит и не проверяет ваш id тип. Он просто предоставляет вам все возможные селекторы. Вы сказали, что эта проблема связана с импортом файла "-Swift.h". В этом случае проверьте ваш код Swift, вероятно, у вас есть count функция, видимая для Objective C, которая возвращает что-то еще, чем Int,

Также вы можете проверить проблему в Issue navigator, выберите его, и он покажет все count вызовы видимы в Objective C. Проверьте их все, большинство из них возвратит NSUInteger, но должен быть один, который возвращает что-то еще, например:

SWIFT_CLASS("_TtC3dev19YourClass")
@interface YourClass : NSObject
- (int32_t)count SWIFT_WARN_UNUSED_RESULT;
@end

Другие вопросы по тегам