Почему компилятор Objective-C должен знать сигнатуры методов?


Почему компилятор Objective-C должен знать во время компиляции сигнатуру методов, которые будут вызываться для объектов, когда он может отложить это до времени выполнения (т. е. динамическое связывание)? Например, если я пишу [foo someMethod], то зачем компилятору знать сигнатуру someMethod?

1 4

1 ответ:

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

Возможно, Вам сказали, что [foo someMethod] преобразуется в вызов функции:

objc_msgSend(foo, @selector(someMethod))

Это, однако, не точно верно. Он может быть преобразован в несколько различных вызовов функций в зависимости от того, что он возвращает (и то, что возвращается, имеет значение, используете ли Вы результат или нет). Например, если он возвращает объект или целое число, он будет используйте objc_msgSend, но если он возвращает структуру (как на ARM, так и на Intel), он будет использовать objc_msgSend_stret, а если он возвращает плавающую точку на Intel (но не ARM, я полагаю), он будет использовать objc_msgSend_fpret. Это все потому, что на разных процессорах соглашения о вызове (как вы настраиваете стек и регистры, и где хранится результат) различны в зависимости от результата.

Также имеет значение, каковы параметры и сколько их (число может быть выведено из имен методов ObjC, если они не являются varargs... правильно, надо заниматься тоже с varargs,). На некоторых процессорах первые несколько параметров могут быть помещены в регистры, в то время как более поздние параметры могут быть помещены в стек. Если ваша функция принимает varargs, то соглашение о вызове может быть еще другим. Все это должно быть известно для компиляции вызова функции.

ObjC может быть реализован как более чистая объектная модель, чтобы избежать всего этого (как это делают другие, более динамичные языки), но это будет стоить производительности (оба пространства и время). ObjC может делать вызовы методов удивительно дешевыми, учитывая уровень динамической диспетчеризации, и может легко работать с чистыми типами машин C, но стоимость этого заключается в том, что мы должны позволить компилятору узнать больше специфики о наших сигнатурах методов.

Кстати, это может привести (и очень часто приводит) к действительно ужасным ошибкам. Если у вас есть несколько методов:

- (MyPointObject *)point;

- (CGPoint)point;

Возможно, они определены в совершенно разных файлах как методы для разных классов. Но если компилятор выберет неправильное определение (например, когда вы отправляете сообщение в id), то результат, который вы получаете от -point, может быть полным мусором. Это очень, очень трудно понять, когда это происходит (и у меня это случалось со мной).

Для немного большего фона, вы можете наслаждаться статьей Грега Паркера , объясняющей objc_msgSend_stret и objc_msgSend_fpret. Майк Эш также имеетотличное введение к этой теме. И если ты хочешь забраться поглубже в эту кроличью нору ... , вы можете увидеть исследование objc_msgsend по инструкции bbum. Он устарел сейчас, до ARC, и охватывает только x86_64 (поскольку каждая архитектура нуждается в своей собственной реализации), но по-прежнему высокообразован и рекомендуется.