Внутреннее устройство приложения Mac для разработчика Windows: часть 1.
Все началось с программы hello world в win32, и это было моим введением в создание приложений с графическим интерфейсом.
Первоначальным источником вдохновения для него послужил Чарльз Петцольд и его легендарная книга Окна программирования. Программа Hello world из Первой главы, вероятно, является наиболее упрощенным представлением программы с графическим интерфейсом.
Псевдокод будет выглядеть так, как показано ниже.
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { RegisterClass(hInstance); // Creates a WNDCLASS structure basically the NSWindow kind of // main class if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } //Then there comes the (message loop) == NSRunloop while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } exit(msg.wParam); }
Dispatch Message находит окно и соответствующий WindowProc и отправляет туда сообщение, которое будет обработано, что-то вроде this.
Mac OS X также имеет аналогичную структуру с другой реализацией. Итак, я начал писать простое приложение с нажатием кнопки и кнопки, чтобы понять, могу ли я имитировать программу win32, а также, возможно, понять основы оконной системы в mac os x.
Для навигации по этой статье я использовал два образца.
a. Приложение UIapplicationbareBone: это приложение не использует никаких файлов .xib, plist просто использует стандартную функцию main и цикл сообщений, как показано выше.
b. Приложение SampleBareBone: это приложение заменяет стандартный класс NSApplication на MyApplication, который реализует функцию запуска, имеющую цикл сообщений.
Первый фрагмент кода приложения показан ниже.
Как показано выше, первая строка - это вызов sharedApplication.
Выполняет следующие действия.
1. InitializeAppContext, как показано ниже, устанавливает eventHandlers.
0x7fff3bca7201 <+141>: callq 0x7fff3c8b08c8 ; symbol stub for: InstallEventHandler 0x7fff3bca7206 <+146>: callq 0x7fff3bca7c42 ; _NSInstallCocoaAppApplicationLevelEventHandlers 0x7fff3bca720b <+151>: movl 0x5c623c9f(%rip), %eax ; _cgsContextID
2. Добавляет CFRunLoopSource пытается зарегистрироваться в цикле выполнения, который может быть запущен извне для работы с событием. Apple docs на самом деле дает очень точное описание того же. Будет несколько вызовов CFRunLoopSource из [NSApplication sharedApplication] каждый раз, когда добавляется источник для другой части стека приложений, трассировки ниже показывают то же самое.
Еще один интересный факт о графической оболочке оконных систем привязан к серверу Windows. В Windows это проводник Windows, в Mac OS x - Finder (когда мы завершаем процесс поиска, вы увидите весь экран обновляется при перезапуске сервера Windows, то же самое происходит с проводником в Windows.), а для IOS это трамплин. По словам Джонатана Левина, автора книги Mac Os X and IOS internals говорится о нескольких серверах Windows на Mac OS X, чтобы снизить нагрузку на сервер окон.
В любом случае, давайте продолжим путешествие, а теперь пора обсудить цикл сообщений.
Как мы видим в приведенном выше приложении, цикл сообщений довольно прост.
На самом деле [NSApplication run ] делает то же самое, что и в приведенном выше фрагменте кода.
Здесь вступает в игру второе приложение, здесь я заменил класс NSApplication классом MyApplication, реализующим функцию Выполнить. Код будет таким, как показано ниже.
Эта функция [self nextEventMatchingMask: untilDate: inMode: dequeue:] фактически завершает вызов функции CFRunloopRun, которая реализует фактически перекачку / цикл сообщений.
Итак, давайте начнем смотреть, как, к счастью, реализован CFRunLoopRun, исходный код которого для краткости открыт. Я попытался частично показать реализацию и объяснить то же самое.
Давайте разберемся, что такое Runloop в первую очередь согласно документации по яблокам.
Объект CFRunLoop отслеживает источники ввода для задачи и отправляет управление, когда они становятся готовыми к обработке. Примеры источников ввода могут включать пользовательские устройства ввода, сетевые соединения, периодические или отложенные по времени события и асинхронные обратные вызовы.
Он может отслеживать три типа объектов
a. Источники
б. Таймеры
c. Наблюдатели
Итак, согласно утверждению, мы можем ожидать, что такая же работа будет проделана в псевдокоде для CFRunloop. Давайте углубимся, цикл while начинается с
CFRunLoopDoObservers для таймеров и источников, наблюдаемых ранее в обеих функциях, это отправит уведомления до таймеры / источники обрабатываются.
Затем вызывается __CFRunLoopServiceMachPort, и извлекается сообщение типа mach_msg_header_t вместе с livePort типа mach_port_t.
В зависимости от типа порта, конкретная функция берет на себя обслуживание сообщения, большая часть заканчивается на mach_msg.
mach_msg, структура которого показана здесь, выглядит как rpc вид структуры, параметр опции служит указателем направления MACH_SEND_MSG и MACH_RCV_MSG
mach_msg_return_t mach_msg (mach_msg_header_t msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t receive_limit, mach_port_t receive_name, mach_msg_timeout_t timeout, mach_port_t notify); mach_msg_return_t mach_msg_overwrite (mach_msg_header_t* send_msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t receive_limit, mach_port_t receive_name, mach_msg_timeout_t timeout, mach_port_t notify, mach_msg_header_t *receive_msg, mach_msg_size_t receive_msg_size);
Интересно то, что происходит после вызова mach_msg, что привело меня к другой точке останова и еще одной трассировке стека, которую я усек.
br s -n mach_msg
он заканчивается, как мы видим в mach_msg_trap, который является точкой перехода в режим ядра (это то, как далеко я могу пройти).
Нет, что делает [NSApp sendEvent: event]? Здесь, как указано в документации, начинается цепочка респондентов и t его статья заканчивается!
Я вернусь с дополнительным исследованием цепочки респондентов в моем следующем посте < br /> Спасибо, что прошли через это.
Ссылки и инструменты: