Алексей, я давно хотел обсудить с вами эту тему, но руки все не доходили. А тут вы сами просите, так что я не сдержусь.
Для начала не большой preface:
Я работаю в студии a-steroids. Мы занимаемся разработкой игр под iPhone и Android. В ближайшее время к списку присоединится PSP. Среди прочего, я проводил исследование о возмодности декомпиляции iPhone приложений.
Как уже можно догадаться, речь пойдет об objective-с, Apple, Mac OS X и iPhone OS. Или почему я не люблю Apple и предпочитаю писать на С++.
Начнем с неких абстрактных вещей.
Для начала, Mac OS X - одна из самых медленных и ресурсоемких операционных систем на рынке. Этому есть две предпосылки - ядро mach 3 и язык objective-c. Не спешите сразу писать мне гневный ответ, дочитайте сначала до конца. С ядром все просто - отвратительно реализованный механизм IPC приводит к постоянным созданиям копий памяти. Система с большим трудом работает на рабочем iMac 20'' с 1 GB оперативной памяти и Core 2 Duo e8300. В особенности когда речь заходит об исользовании X-Code (к слову MSVS 2005 SP1 на Vista SP1) на примерно аналогичной конфигурации работает значительно быстрей). На моем MacBook 2.1 с 2GB RAM ситуция не сильно лучше. Если быть точным, то примерно через 2 часа использования x-code система становится практически unusable.
А вот теперь более подробно о второй проблеме системы - об objective-с.
Как я уже говорил, я занимался реверсом objective-с кода, как собранного под Arm v6, так и под x86. Во всех случаях код собирался в release режиме с ключом -O2.
Для начала стоит отметить, что objective-с - не совсем язык. Это скорее очень расширенный препроцессор. Вызов собственно objective-с парсера находится перед вызовом cpp (имеется ввиду препроцессор С). Далее же работает сс1 или cc1plus. Это позволяет языку радостно наследовать все костыли С/С++ и добавлять в них свои, персональные. Часть из них правда делается исключительно компилятором
Итак, первый ключевой момент: вызов метода (оно же - посылка сообщения).
[object method:param1 p2:param2] превратится в следующий ассемблер код
push object push methodID ; об этом чуть позже push param1 push param2 call objc_metacall
Из этого уже легко сделать вывод, что посылка сообщения объекту практически равносильна вызову сигнала в Qt. При этом лишена гибкости последнего. Рантайм от Apple правда предоставляет костыль (по другому рука не поднимается назвать), позволяющий добится гибкости модели signal/slot - NSNotificationCentre. Правда в данном случае это обойдется значительно дороже. Впридачу ко всему - это минимум в 2 раза дороже вызова виртуальной функции в с++ (не стоит мне на это рассказывать про возможность получения указателя на С метод. Все адекватные компиляторы с++ в подобных случаях проводят девиртуализацию)
Слегка подробней о methodID. В общем случае это указатель на строку с сигнатурой метода. objc_metacall по object получает его метакласс, находит метод с его сигнатурой (слава богу это кэшируется) и затем передает управление собственно методу.
Второй, и самый страшный момент - рантайм библиотека. Это просто смерть всему живому и жизнь всему мертвому. Во первых, она берет на себя слишком много - от конструирования и уничтожения С++ объектов с нетривиальными деструкторами до эмуляции "стековых" объектов (то есть autorelease). Во вторых ей сопутствует целая куча проблем. Например переполнение RunLoop (подробнее можно почитать на хабре). И все это шлифуется совершено с не ожиданной стороны. Apple'овский as использует LLVM. У этой технологии огромное будущее, но в настоящий момент она абсолютно не предназначена для сборки системных библиотек. В особенности это заметно под x86 архитектурой. Дело в том, что LLVM рассказали, что векторизация кода это крута. И llvm умудряется идеально переносить на sse очень не тривиальные моменты. Однако, по каким-то причинам он иногда забывает про то, что данные должны быть выравнены по границе 16 байт. В частности из-за этого в libstdc++ 4.0 не рабоет std::map (он использует sse для копирования больших блоков). Подобные ошибки приводят hardware исключения. Вкупе с вобщем-то закрытым кодом это приводит к очень серьезным проблемам.
Кстати, касательно "чистоты" objective c кода:
Приведу конкретный пример: у UIView есть property - CGRect frame, описываемый следующим набором структур
struct CGPoint{ CGFloat x; CGFloat y; }; struct CGSize{ CGFloat width; CGFloat height; }; struct CGRect{ CGPoint origin; CGSize size; }; //Следующий код вызовет ошибку компиляции view.frame.origin.x = 1; //А вот такой - нет CGFloat x = view.frame.origin.x; }
После такого, говорить о плохом синтаксисе с++ лично мне не хочется.
Однако и это не все проблемы, порождаемые Mac OS X. Во первых, многие фреймворки истекают памятью. Во вторых, Apple позволяет себе менять поведение методов. При выходе iPhone OS 3.1 Apple умудрилась поменять поведение класса MKView (отвечает за интеграцию с Google Maps) во фреймворке 3.0. И забыли об этом сказать. На поиск ошибки ушел рабочий день. Вкупе с тем, как они принимают приложение в аппстор, в нашем приложении минимум 2 недели будет ошибка на устройтсвах с прошивкой 3.1. При этом аппл хочет 99 долларов за то, что бы просто иметь возможность тестировать свои приложения на девайсе. Это в плюс к компьютеру с минимум двукратной наценкой и собственно девайсу.
По не понятным причинам, для того, что бы поменять XCode 3.1.3 на 3.1.4 мне нужно заново качать 2.4 гигабайта полного дистрибутива. Я бы не стал этого делать, если бы не оказалось, что я не могу отлаживать на девайсе с прошивкой 3.1 приложения, линкуемые с SDK 3.0 из под 3.1.3
Про ошибки в Snow Leopard я вообще. Один coreaudiod, способный сожрать 4 GB памяти чего стоит (кстати на Leopard он как-то умудрился попасть в своп, когда я смотрел фильм)
Отдельное спасибо Apple за 8 сервис паков и 8 секьюрити апдейтов за 1.5 года. Это не считая "обновления прошивки клавиатуры", всяких QuickTime'ов, iTunes'ов (та еще гадость) и прочих мелочей (а не дай бог стоит iLife...). Кстати один из секьюрити апдейтов не нароком разнес мне журнал файловой системы. Аппл потом даже на оффсайте отписывала, что в его инсталяторе была ошибка.
Опять таки, про не тормозящий iPhone. Запустите Сафари, затем майл (естественно для этого придется сафари закрыть). Закройте mail и наслаждайтесь бешенной скоростью работы девайса с 400 MHz процессором и 128 MB RAM. А уж если вы еще не дай бог во что либо еще поиграете...
Например, AFAIK, для мобильных устройств память выделяют и распределеют уже на старте приложения и потом выделений (и работы сборщика мусора) уже нет.
Только на Java ME устройствах. И то это связанно не со спецификой "языка", а с тем, что хип все равно 1. Jit'ed C# и (в ряде случаев) Java зачастую по скорости обходят С++. Говорить в этом контексте об Objective-C как-то вообще не корретно. В "iPhone Games Projects" один из авторов даже писал, что не смотря на то, что он ярый фанат objective-c, его использования в приложениях, где критична скорость стоит минимизировать (к сожалению, совсем избавиться от него нельзя). В противном случае о скорости и эффективном использовании памяти стоит забыть.
Часть из этого является моим личным мнением. Большая же часть - реальные факты.
PS Большая часть того, что вы называете "идотскими глюками" С++ не вызвало у меня вопросов даже на этапе начального изучения.
PPS Если moc - костыль, то что же тогда Objective-C?