System ui plugin framework mac os x objective c

Интеграция приложений Qt в среду Mac OS X (с использованием Cocoa и Objective-C++)

Доброго всем дня!

Недавно я писал о кастомизации заголовка окна в Mac OS X и получил реквесты написать поподробнее о взаимодействии Qt и Cocoa. Думаю, тему можно немного развернуть и написать об интеграции приложений, написанных с помощью Qt, в среду Mac OS X. Оговорюсь, что используется в данном случае Qt for Cocoa, если возьмёте Qt for Carbon, то и работать придётся только с карбоном. Но он морально устарел, и использовать его стоит только в крайних случаях.

Обычная Qt-программа имеет ряд несостыковок с Apple HIG. Точнее, может иметь, так как не всем программам нужен дополнительный функционал. Например, не любой программе надо иметь бэдж поверх значка в доке, расширять меню дока или выносить/дублировать некоторые функции в маковское меню.

Но что делать, если такой функционал нужен? Если нужно отображать в доке количество уведомлений (а-ля скайп), обрабатывать клик по иконке в доке, добавлять свои пункты меню в док, да ещё и иметь нормальное меню, в общем, сделать так, чтобы программа смотрелась как родная в Mac OS? Что-то из этого можно сделать с помощью штатных или полудокументированных функций Qt, а что-то — только с использованием Cocoa и, соответственно, Objective-C… Что же делать?

Нам поможет Objective-C++!

Что это за зверь и с чем его едят? По сути, это возможность комбинировать Objective-C и C++ классы в одном файле исходников. Причём, расширение заголовочников остаётся стандартным (.h), а вот для исходников нужно указывать расширение .mm, чтобы компилятор его съел и не поперхнулся.

Теперь представим себе, что у нас есть некий (может быть даже большой) проект, написанный с помощью Qt. Изначально он писался под винду или линукс, а вот теперь его надо перенести в макось, да так, чтобы было красиво и удобно, чтобы маководы не морщили нос при виде этого чудища.

Понятно, что для начала надо подстроить интерфейс программы под Apple HIG, без этого никуда, но это останется за рамками данной статьи, упомяну лишь полезные дефайны Q_WS_*, которые позволяют компилить разный код для разных ОСей. Мы же будем говорить о том, какими средствами можно подстроить своё приложение под новое окружение (или же как создать Mac-приложение на Qt с нуля — это зависит от поставленных целей).

Итак, пойдём по порядку.

Общая интеграция

Для начала дадим имя и значок программе. Нет, не то имя, которое имеет бандл, а имя, которое будет отображаться в Application Menu. Для этого нам потребуется свой файл Info.plist, а не тот, что генерирует qmake. Это делается одной строчкой в .pro:

В наш .plist пишем что-то такое:

Разумеется, вместо «MyAppName» и «com.mycompany.myapp» пишем английское название программы и свой идентификатор бандла. Почему английское? Да потому что локализуем мы его в другом файле. На это указывает самый первый параметр в плисте: LSHasLocalizedDisplayName. Создаём директорию «ru.lproj», а в ней файл InfoPlist.strings. В этот файл пишем что-то такое:

Здесь уже нужно указывать локализованное название бандла и имя, отображаемое в Application Menu. Чтобы это заработало, нужно при установке программы скопировать эту дерикторию в AppName.app/Contents/Resources, делать это лучше через указание INSTALLS в .pro файле, за подробностями прошу обращаться к документации к qmake. На скриншоте видно, что Application Menu имеет русское название, несмотря на то, что сам бандл имеет название на латинице.


Чтобы задать иконку программы (см. myicon.icns в файле MyInfo.plist), нам нужен файл .icns, который можно создать самому в графическом редакторе, либо сконвертировать из .ico или кучки .png с помощью программ или онлайн сервисов. Чтобы смотрелась иконка хорошо, лучше сделать в ней несколько размеров: 512×512, 256×256, 128×128, 64×64, 48×48, 32×32, 16×16. Система сама выберет какой размер в какой ситуации отображать. Файл с иконкой надо так же устанавливать в Resources. Самый простой способ заставить его устанавливаться — это прописать следующее в .pro файле:

Разделение кода

У Objective-C++ есть множество ограничений. Нельзя, к примеру, включать хедер с объявлением интерфейса Objective-C класса в файл исходников .cpp, ибо компилятор подавится. Для новых проектов, расчитанных только на Mac OS X, это не будет ограничением, ибо можно весь C++ код держать в .mm файлах и радоваться жизни. Но смысл Qt в кроссплатформенности, так что будем считать, что наши исходники всё-таки в .cpp файлах.

Выход тут прост. Надо создать «обёртку» для Objective-C вызовов и классов на C++. Я выбрал такую структуру: есть Qt/C++ класс, который обеспечивает интеграцию с Mac OS X. Какую-то работу он выполняет сам, а какую-то перепоручает «делегату», приватному классу, активно использующему Cocoa/Objective-C. При этом, получаем следующие файлы:

* myclass.h — заголовок основного класса
* myclass.cpp — реализация
* myclass_p.h — заголовок приватного класса (без Objective-C-интерфейсов)
* myclass_p.mm — исходники приватного класса, могут включать в себя интерфейсы и имплементацию классов Objective-C, включать любые хедеры и т.п.

Таким образом, мы чётко разграничиваем C++ и Objective-C++.

Кстати, чтобы Objective-C++ заработал, в .pro файле надо все хедеры/исходники, использующие его, помещать в секции OBJECTIVE_HEADERS и OBJECTIVE_SORCES. И, разумеется, делать это внутри блока macx: <>. А ещё, если мы хотим использовать Cocoa, то надо добавить этот фреймворк к проекту. Пишем в .pro:

А теперь пойдёт интересное.

Работа с Dock

Рассмотрим пять основных функций работы с доком: добавление бэджа, добавление оверлея, обработка клика на иконке в доке, «подбрасывание» иконки программы в доке и добавление своего меню. Средствами Qt решить можно только последнюю проблему, да и то слабо документированной функцией qt_mac_set_dock_menu(QMenu*). Причём объявить её надо самим, как внешнюю:

На меню, исходя из личного опыта, накладываются некоторые ограничения в сравнении с «родным» маковским меню:

* неактивные (disabled) пункты меню становятся зачем-то активными
* QMenu не эмиттит сигналы aboutToHide и aboutToShow
* нельзя сделать отступ каких-либо элементов
* если первый QAction в меню — разделитель, то он будет виден (в отличие от всех остальных проявлений QMenu)

Так что под это придётся подстраиваться. Можно, конечно, сделать всё на Objective-C/Cocoa, но тогда придётся делать свой механизм маппинга QAction’ов и родных пунктов меню. Но это имеет смысл делать только при действительно большой необходимости в устранении указанных ограничений.


Рассмотрим теперь клик на доке. Если бы мы писали на Cocoa, то проблем бы не было, достаточно было бы в AppDelegate реализовать метод applicationShouldHandleReopen:hasVisibleWindows:. Но мы не имеем прямого доступа к делегату программы, созданному в недрах Qt. Поэтому воспользуемся магией рантайма для добавления реализации этого метода. Для начала объявим функцию, которая будет реализовывать то, что нам нужно. Для этого превратим наш приватный класс в синглтон (всё равно нам не нужно более одного объекта этого класса) и напишем такую функцию:

Читайте также:  Команды касперского для линукс

Функция мертва без внедрения её в делегата. Не будем же с этим медлить!

Здесь мы берём класс делегата нашего приложения и добавляем в него метод на лету. И заодно реализуем метод emitClick(), эмиттящий Qt-сигнал о клике. Собственно, вот и всё, теперь по клику в доке мы можем показывать, к примеру, главное окно программы.

Далее можно попробовать подбросить иконку программы в доке. Первая же мысль: «так это же умеет делать QApplication::alert(QWidget*)!» Мысль верная, но преждевременно оптимистичная. Всё дело в том, что в Mac OS X 10.6.* эта функция работает как надо, а вот в 10.7.* почему-то не хочет (может быть, это связано с тем, что Qt 4.7.x официально не поддерживает Lion, а в 4.8 это пофиксят). Я не стал разбираться почему так происходит, и просто написал подбрасывание на Cocoa, благо это делается одной строкой:

И пусть этот кусок кода дублирует Qt-шный alert(), зато работать будет во всех версиях Mac OS X. А для других систем можно по-прежнему использовать alert().

Теперь разберёмся с бэджем. Если кто не в курсе, то это крсный кружок с текстом, отображаемый поверх значка программы в доке. Например, он используется для отображения числа непрочитанных писем в Mail.app. Документация от Apple говорит, что делать это надо так:

Здесь badgeString имеет тип NSString*. Ага, вот и первое неудобство! В Qt обычно идёт манипулирование QString, а значит, надо написать некий «конвертер» строк:

Как видно из кода (и из комментария к нему), возвращаемая строка не релизится, так что придётся делать это в вызывающем коде (Qt не использует ARC, так что за памятью следить будем сами).

Теперь можем написать функцию нашего приватного класса, которая будет выводить нужную нам строку в бэдже дока:


Теперь последний аспект работы с доком — добавить произвольный оверлей в док. Это делается с помощью следующего кода:

Здесь view это NSView*. Но мы-то работаем с Qt! А значит, нам надо в оверлей поместить QWidget*. Как же получить из QWidget’а его NSView? Пишем простую функцию:

Всё просто, создатели Qt сделали почти всю работу за нас.

Но, увы, ждёт нас облом: NSView, полученный из QWidget’а непригоден для установки в док. То есть, он ставится, NSDockTile его съедает, но вместо содержимого виджета в доке образуется пустое место. Не знаю уж, почему. Но даже в Qt Creator’е прогресс-бар в док вешается именно через свой чистый NSView, созданный специально для этого. Так что, если нужен свой оверлей, то милости просим написать свой View на Cocoa. Увы.

Работа с меню

Перейдём к маковскому меню (тому, что на верхней панели). По умолчанию, Qt-программа его не создаёт (ну, если не считать стандартного Application Menu с системными функциями). Первое, что приходит на ум Qt-разработчику, это QMenuBar. И действительно, в документации к нему написано, что он может выполнять функции маковского меню. Но тут два варианта: либо сделать отдельный QMenuBar для каждого окна программы, либо сделать один глобальный. Выберем второй вариант в силу его неоспоримых преимуществ.

Опять же, исходя из документации, нам нужно создать QMenuBar с нулевым родителем, т.е., QMenuBar * macMenuBar = new QMenuBar(NULL), тогда он будет глобальным. Точнее, первый, из соданных таким образом менюбаров, станет глобальным.

А теперь — куча монотонной работы руками. Создаём сами меню «Правка», «Файл», «Справка» и так далее. Это достаточно нудная работа, но без неё программа хорошо выглядеть не будет. Кроме того, стандартные сочетания клавиш ⌘W и ⌘M не будут соответственно закрывать и сворачивать окна. Их придётся так же делать самостоятельно (хорошо хоть ⌘Q работает сразу).

Отмечу некоторые особенности QAction’ов в Mac OS X. Если им задавать шорткаты, то в конструктор QKeySequence надо передавать «Ctrl+M» для создания шортката «⌘M». И вообще, клавиша «⌘» в Qt под мак везде проходит как Ctrl, а клавиша «Ctrl» — как Meta. Для более лёгкой портируемости программ, надо полагать.

Ну и об ограничениях данного подхода к созданию меню.

* нельзя сделать отступ пунктов меню
* QMenu не эмиттит сигнал aboutToHide (только aboutToShow)
* имеет место следующий баг: если меню «Справка» называется «Help», то в нём будет автоматически создан системный блок поиска по пунктам меню, но если он будет назван по-русски, этого не произойдёт, даже если в системе текущая локаль — русская. Как избавиться от этого глюка, я пока не нашёл.

Почти финал — кастомный заголовок окна в Qt

Чтобы применить всё, написанное мной в прошлой статье, к Qt-программе, нужно сделать следующее. Во-первых, ставим где надо retain/release, т.к. не включен ARC. Во-вторых, в Objective-C++ разрешено то, что не удалось сделать в чистом Objective-C: взятие класса, если он объявлен только форвардом:

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

Заключение

Итак, мы рассмотрели Objective-C++ в применении к связке Qt+Cocoa для интеграции программы, написанной на Qt, в среду Mac OS X. По большому счёту, ничего сложного в этом нет, если имеются базовые знания Objective-C и Cocoa. Надо только знать некоторые особенности, на которых я постарался заострить внимание в данной статье.

Если кого интересуют полные исходники тестового проекта, то добро пожаловать на GitHub!

PS: ещё можно было бы рассмотреть встраивание в программу уведомлений через Growl, но это читатель может сделать и сам, если он усвоил материал.

Источник

Создание программ для Mac OS X. Часть 1: вступление и Objective-C

Я думаю все слышали про Mac OS X как операционную систему для дизайнеров и домохозяек. Но хочется рассказать про средства разработки для OS X, а то хорошие программы пишут, а на чем — никто не знает.

Сразу скажу, что не буду рассказывать про кроссплатформенные фреймворки и тулкиты(такие как Qt) или про создание консольных приложений, я расскажу про то, что в плане создания приложений отличает Mac OS X от других операционных систем, а именно — фреймворк Cocoa. Оговорюсь сразу, что буду стараться избегать сравнений с другими фреймворками, я хочу просто рассказать про Cocoa.

Заглянем немного в историю. Mac OS X — дальнейшее развитие ОС NextSTEP. NextSTEP была первой ОС в которой очень широко использовался язык Objective-C, на нем была написана большая библиотека готовых объектов, причем как и обычные типы данных — строки, массивы, словари, так и объекты, используемые для построения GUI приложений. Поэтому, большая часть приложений под NextSTEP писалась на Objective-C с использованием готовых объектов. Эта самая библиотека и переросла в Cocoa framework.

Но включать в новую ОС почти незнакомый никому API было бы крайне плохим решением, поэтому добавили еще 2: Classic и Carbon.

Classic создан для того, чтобы запускались приложения Mac OS 9, на данный момент рассматривать его бессмысленно, т.к. после перехода на процессоры фирмы Intel по понятным причинам Classic был выкинут из системы.

Читайте также:  Windows 10 home можно ли использовать юридическим лицам

Carbon был создан чтобы легко перенести приложения из Mac OS 9 в OS X, с возможностью добавить к уже готовому коду программы новую функциональность, доступную только в десятке. Как ни странно, но много приложений до сих пор написаны на Carbon(например MS Office for Mac и некоторые продукты Adobe).

На данный момент фреймворки Carbon и Cocoa развиваются паралельно, но со следующего релиза Mac OS X будут развивать только Cocoa.

Основным языком разработки под Cocoa является Objective-C, а поскольку в дальнейшем все примеры будут идти на этом языке, то впервой части я расскажу именно про него. Но если вы уже владеете Python или Ruby, то изучать Objective-C вам не надо, в XCode 3.0(срада разработки, о ней в следующей части) биндинги для этих языков «искаропки».

Язык программирования Objective-C

Помимо широкого известного и распространенного объектного расширения языка С — языка С++ — есть и другое его расширение — язык Objective-C, обладающий огромной простотой, полной совместимостью с языком С и очень мощной и выразительной объектной моделью, заимствованной из языка Smalltalk.

Язык был придуман Брэдом Коксом (Brad Cox) в начале 80-х годов прошлого века. Целью Кокса было создание языка, поддерживающего концепцию software IC. Под этой концепцией понимается возможность собирать программы из готовых компонент (объектов), подобно тому как сложные электронные устройства могут быть легко собраны из набора готовых интегральных микросхем (IC, integrated curcuits). При этом такой язык должен быть достаточно простым и основанным на языке С, чтобы облегчить переход разработчиков на него.

Одной из целей было также создание модели, в которой сами классы также являются полноценными объектами, поддерживалась бы интроспекция и динамическая обработка сообщений.

Получившийся в результате язык Objective-C оказался крайне прост — его освоение у С-программиста займет всего несколько дней. Он является именно расширением языка С — в язык С просто добавлены новые возможности для объектно-ориентированного программирования. При этом любая программа на С является программой и на Objective-C (для языка С++ это не верно).

Еще одной из особенностей языка является то, что он message-oriented в то время как С++ — function-oriented. Это значит, что в нем вызовы метода интерпретируются не как вызов функции (хотя к этому обычно все сводится), а именно как посылка сообщения (с именем и аргументами) объекту, подобно тому, как это происходит в Smalltalk-е. Такой подход дает целый ряд плюсов — так любому объекту можно послать любое сообщение. Объект может вместо обработки сообщения просто переслать его другому объекту для обработки (так называемое делегирование), в частности именно так можно легко реализовать распределенные объекты (т.е. объекты находящиеся в различных адресных пространствах и даже на разных компьютерах). Привязка сообщения к соответствующей функции происходит непосредственно на этапе выполнения.

Язык Objective-C поддерживает работу с метаинформацией — так у объекта непосредственно на этапе выполнения можно спросить его класс, список методов (с типами передаваемых аргументов) и instance-переменных, проверить, является ли класс потомком заданного и поддерживает ли он заданный протокол и т.п.

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

На данный момент язык Objective-C поддерживается компилятором gcc. Довольно много в языке перенесено на runtime-библиотеку и сильно зависит от нее. Вместе с компилятором gcc поставляется минимальный вариант такой библиотеки. Также можно свободно скачать runtime-библиотеку от компании Apple: Apple’s Objective-C runtime. Эти две runtime-библиотеки довольно похожи (в основном отличие заключается в именах методов), хотя далее я буду ориентироваться на runtime-библиотеку от компании Apple.

Синтаксис языка

В языке Objective-C для обозначения объектов используется специальный тип id. Переменная типа id фактически является указателем на произвольный объект. Для обозначения нулевого указателя на объект используется константа nil. Кстати про id: движок игры Doom разрабатывался на рабочих станциях Next, так что может есть связь между типом id и названием idSoftware.

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

Для посылки сообщений используется следующий синтаксис:
[receiver message];

Сообщение может также содержать параметры:
[myRect setOrigin:30.0 :50.0];
В этом примере именем метода (сообщения) является setOrigin. Обратите внимание, что каждому передаваемому аргументу соответствует ровно одно двоеточие. При этом в приведенном примере первый аргумент имеет метку (текст перед двоеточием), а второй — нет.

Язык Objective-C позволяет снабжать метками каждый аргумент, что заметно повышает читаемость кода и снижает вероятность передачи неправильного параметра.
[myRect setWidth:10.0 height:20.0];
В этом примере в качестве имени сообщения выступает setWidth:height:.

Также поддерживается возможность передачи произвольного количества аргументов в сообщении:
[myObject makeGroup: obj1, obj2, obj3, obj4, nil];

Как и функции, сообщения могут возвращать значения, при этом в отличии от языка С, типом возвращаемым по умолчанию значения является id.
float area = [myRect area];

Результат одного сообщения можно сразу же использовать в другом сообщении:
[myRect setColor:[otherRect color]];

Как уже говорилось, в Objective-C классы сами являются объектами. Основной задачей таких объектов (называемых class objects) является создание экземпляров данного класса. При этом само имя класса играет двойную роль — с одной стороны оно выступает как тип данных (т.е. он может быть использован для описания указателей на объекты данного класса). А с другой стороны имя класса может выступать в качестве объекта, которому посылается сообщение ( в сообщениях имя класса может принимать участие только как receiver). В языке Objective-C нет встроенного типа для булевских величин, поэтому обычно такой тип вводится искусственно. Далее я буду для логических величин использовать тип BOOL с возможными значениями YES и NO(ИМХО более понятней, но не так “политкорректно” как true/false).

Создание новых классов

Все новые директивы компилятору в языке Objective-C начинаются с символа @. Как и в С++ описание класса и его реализация разделены (обычно описание помещается в заголовочные файлы с расширением h, а реализации — в файлы с расширением m).
Ниже приводится общая структура описания нового класса:
@interface ClassName: SuperClass
<
instance variable declarations
>
method declarations
end

В версии runtime от Apple все классы имеют общего предка — класс NSObject, содержащий целый ряд важных методов. Описание переменных ничем не отличается от описания переменных в структурах в языке С:
@interface Rect: NSObject
<
float width;
float height;
BOOL isFilled;
NSColor * color;
>
end

Каждое описание начинается со знака плюс или минус. Знак плюс обозначает, что данный метод является методом класса (т.е. его можно посылать только class object’у, а не экземплярам данного класса). Фактически методы класса являются аналогами статических методов в классах в языке С++. Знак минус служит для обозначения методов объектов — экземпляров данного класса. Обратите внимание, что в Objective-C все методы являются виртуальными, т.е. могут быть переопределены.

Читайте также:  Яндекс disk для windows

Ниже приводятся описания возможных методов для класса Rect.
@interface Rect: NSObject
<
float x, y;
float width;
float height;
BOOL isFilled;
NSColor * color;
>
+ newRect;
— (void) display;
— (float) width;
— (float) height;
— (float) area;
— (void) setWidth: (float) theWidth;
— (void) setHeight: (float) theHeight;
— (void) setX: (float) theX y: (float) theY;
end

Обратите внимание, что имя метода может совпадать с именем instance-переменной данного класса (например, width и heigh).
Тип возвращаемого методом значения указывается в круглых скобках сразу же после знака плюс или минус (но перед названием метода). Если тип не указан, то считается, что возвращается значение типа id. Далее идет имя метода, где после каждого двоеточия задается тип аргумента (в круглых скобках) и сам аргумент. Язык Objective-C позволяет для аргументов метода задавать также один из следующих описателей — oneway, in, out, inout, bycopy и byref. Данные описатели служат для задания направления передачи данных и способа передачи.

Метод, принимающий произвольное количество параметров, может быть описан следующим образом:
— makeGroup: (id) object, . ;

Для подключения заголовочного файла в Objective-C вместо директивы #include используется директива #import, полностью аналогичная #include, но гарантирующая что данных файл будет подключен всего один раз.

Реализация методов класса выглядит следующим образом:
#import «ClassName.h»
@implmentation ClassName
method implementations
end

Ниже приводится пример реализации методов класса Rect, описанного ранее.
#import «Rect.h»

+ newRect <
Rect * rect = [[Rect alloc] init];
[rect setWidth: 1.0f];
[rect setHeight: 1.0f];
[rect setX: 0.0f y: 0.0f];
>
— (float) width < return width; >
— (float) height < return height; >
— (float) area < return [self width] * [self height]; >
— (void) setWidth: (float) theWidth < width = theWidth; >
— (void) setHeight: (float) theHeight < height = theHeight; >
— (void) setX: (float) theX y: (float) theY <
x = theX;
y = theY;
>
end

Как видно из примера выше, в методах доступны все instance-переменные. Однако, как и в С++, есть возможность управлять видимостью переменных (видимостью методов управлять нельзя) при помощи директив private, protected и public (действующих полностью аналогично языку С++).

Как работает механизм сообщений

Компилятор переводит каждую посылку сообщения, т.е. конструкцию вида [object msg] в вызов функции objc_msgSend.

Эта функция в качестве своего первого параметра принимает указатель на объект-получатель сообщения, в качестве второго параметра выступает т.н. селектор, служащий для идентификации посылаемого сообщения. Если в сообщении присутствуют аргументы, то они также передаются objc_msgSend как третий, четвертый и т.д. параметры. Далее происходит поиск подходящей функции среди функций данного класса, если такой не найдено, то среди функций родительского класса, если и там не найдено, то среди функций родительского класса родительского класса( 🙂 ) и т.д. Если метод (т.е. соответствующая ему функция) находится, то осуществляется его вызов с передачей всех необходимых аргументов.

В противном случае объекту дается последний шанс обработать сообщение перед вызовом исключения — селектор сообщения вместе с параметрами «заворачивается» в специальный объект типа NSInvocation и объекту посылается сообщение forwardInvocation:, где в качестве параметра выступает объект класса NSInvocation.

Если объект поддерживает forwardInvocation:, то он может либо сам обработать посылаемое сообщение, либо переслать другому объекту для обработки:
— (void)forwardInvocation:(NSInvocation *)anInvocation
<
if ( [someOtherObject respondsToSelector: [anInvocation selector]] )
[anInvocation invokeWithTarget: someOtherObject];
else

>

Создание и уничтожение объектов

В самом языке Objective-C нет специальных команд для создания и уничтожения объектов (подобных new и delete). Эта задача ложится на runtime-библиотеку и реализуется при помощи механизма посылки сообщений.
Создание нового объекта разбивается на два шага — выделение памяти и инициализация объекта. Первый шаг реализуется методом класса alloc (реализованном в классе NSObject), который выделяет необходимое количество памяти (данный метод используется для выделения памяти не только для объектов класса NSObject, но и любого унаследованного от него класса). При этом выделяемая память обнуляется и в атрибут isa записывается указатель на class object соответствующего класса.

Обратите внимание, что сообщение alloc посылается class object-у требуемого класса и это сообщение возвращает указатель на выделенную под объект память.

Собственно сама инициализация объекта (т.е. установка значений его instance-переменных, выделение дополнительных ресурсов и т.п.) осуществляется другими методами, по традиции имена этих методов начинаются с init. Обычно такое сообщение посылается сразу же после сообщение alloc, по адресу, возвращенному этим сообщением.

id anObject = [[Rectangle alloc] init];

При создании нового класса обычно нет необходимости переопределять метод alloc, а вот необходимость переопределения метода init возникает достаточно часто (хотя во многих случаях можно положится на обнуление памяти alloc’ом).

Обратите внимание, что метод(ы) init является обычным методом, ничем не выделяющимся среди остальных (в отличии от С++, где конструктор — это особый метод, у которого например нельзя взять адрес). Поэтому при создании нового класса и метода init вызов переопределенного метода init (при помощи [super init]) должен быть произведен явно в самом начале метода.

Mac OS X (как и NextStep) для управления временем жизни объектов используют reference counting — каждый объект содержит внутри себя некоторый счетчик, при создании устанавливаемый в единицу.

Посылка объекту сообщения retain увеличивает значение этого счетчика на единицу (так все контейнерные классы библиотеки Foundation при помещении в них объекта, посылают ему сообщение retain). Установившейся практикой является посылка объекту сообщения retain всеми, заинтересованными в нем сторонами (объектами), т.е. если вы запоминаете ссылку на объект, то следует послать ему сообщение retain.

Когда объект перестает быть нужен, то ему просто посылается сообщение release. Данное сообщение уменьшает значение счетчика на единицу и, если это значение стало меньше единицы, уничтожает данный объект.

Перед уничтожением объекта ему посылается сообщение dealloc, позволяющее объекту произвести свою деинициализацию. При этом это также является обычным сообщением и в нем Вы явно должны в конце вызвать унаследованную реализацию через [super dealloc].

Objective-C 2.0

На WDC2006 Apple представила новую версию языка — 2.0. Среди нововведений были отмечены сборка мусора, быстрая энумерация, свойства в классах, 64-битная поддержка и многое другое. Следует отметить, что эти нововведения доступны только для Leopard.

Сборка мусора

Objective-C 2.0 позволяет производить автоматическую сборку мусора, правда это опционально.

Свойства

Ранее для изменения и чтения instance variables необходимо было писать методы возврата и задания значения(т.н. getters and setters), теперь можно писать так:

@interface Person: NSObject <
>
@property(readonly) NSString *name;
@property(readonly) int age;
-(id)initWithName:(NSString)name age:(int)age;
end

Получить имя можно так:
NSString *name = aPerson.name;

Быстрая энумерация

Теперь добавлен аналог оператора foreach:
for (Person *p in thePeople) NSLog(@»%@ is %i years old.», [p getName], [p getAge]);

Для первой части хватит. При составлении статьи были использованы материалы сайтов developer.apple.com и steps3d.narod.ru(кстати единственный сайт, на котором есть информация про программирование в Mac OS X на русском).

В следующей части расскажу про среду разработки XCode и редактор интерфейсов Interface Builder, а также покажу создание совсем простенького приложения.

Источник

Оцените статью