Как тип "id" понимает получатель метода без приведения?
После слияния master с моей рабочей веткой я получил ошибку компилятора в строке, которая не была изменена. Ошибка выглядит
id test;
[test count];
Найдено несколько методов с именем 'count' с ошибочным результатом.
Сначала это выглядит ясно, потому что компилятор не знает, к какому конкретному типу относится переменная "test". Но я не понимаю, почему это сработало раньше.
Если я создаю новый файл, эта строка работает, предполагая, что это метод NSArray. Почему компилятор не показывает ошибку в этом случае?
При отображении сообщения об ошибке показано несколько возможных приемников метода подсчета. (NSArray, NSDictionary, NSSet) Ищет ли он все классы, которые могут получить это сообщение и показать ошибку, если их несколько?
Я заметил, что ошибка возникает при импорте файла "-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