Objective-C для программистов C++. Часть 1
Перевод серии статей David Chisnall «Objective-C for C++ Programmers»: Часть первая, часть вторая, часть третья
http://netsago.org/ru/docs/1/15/
Objective-C был спроектирован как минимальный набор расширений для C, предоставляющих семантику, схожую со Smalltalk. Он также включает в себя схожий со Smalltalk синтаксис, с помощью которого проще увидеть, какая часть кода является чистым C, а какая — содержит расширения Objective-C.
В отличие от C++, Objective-C — это расширенный набор C. Каждая программа, написанная на C, является корректной программой Objective-C. Все новые ключевые слова в Objective-C начинаются с символа @, который не является корректным идентификатором C.
Система рантайма
Первой реализацией Objective-C был препроцессор, который переводил Objective-C в C. Все динамические возможности языка поддерживались библиотекой рантайма. Современные компиляторы не использую C в качестве промежуточного звена, но они сохранили такой же порядок работы с библиотекой рантайма, предоставляющей динамические возможности.
В Objective-C объектами являются простые структуры на C, в которых первый элемент — указатель на другую структуру на C, представляющую класс. Что именно содержит эта структура класса — немного различается по времени запуска, но как минимум она содержит информацию для поиска методов, оболочку для переменных экземпляра («полей класса», говоря терминами C++) и некоторые другие метаданные.
Так как Objective-C является расширенным набором расширений над чистым C, а все возможности библиотеки рантайма (включая структуру классов) раскрываются в C, — это значит, что Objective-C полностью открывает объектную модель программисту. Нет никакой магии в Objective-C. Когда вы вызываете метод в C++, компилятор делает некоторые соответствующие действия с виртуальными таблицами, чтобы найти корректный код для запуска. В Objective-C, он делает что-то одно из следующих вариантов:
• Вызовет функцию C для поиска функции, реализующий метод (с рантайм GNU)
• Вызовет функцию C, которая ищет метод и вызывает его в одну операцию (с рантайм Apple).
Это значит, что вы можете писать чистый код на C, который взаимодействует с объектами Objective-C. Это также позволяет вам обходить динамическое поведение тогда, когда оно не нужно.
Немного истории
Objective-C был создан в ранних 1980-х Брэдом Коксом (Brad Cox), как набор минимальных расширений к C для просто поддержки объектов в стиле Smalltalk. Он вырос из проекта Object Oriented Pre-Compiler (OOPC), который имел очень похожую семантику, но действительно ужасный синтаксис.
Первоначальная реализация и торговая марка Objective-C была куплена компанией NeXT, и язык использовался повсеместно в их новой операционной системе. В NeXT на Objective-C сделали GUI тулкит и построитель интерфейсов. Это была первая коммерческая утилита для быстрой разработки приложений (Rapid Application Development, RAD). У неё также был хороший побочный эффект, заключавшийся в том, что построитель интерфейса не хранил одно только описание интерфейса — он хранил сериализованные объекты.
В ранних 1990-х NeXT и Sun сотрудничали в области разработки портативной версии программной среды NeXT. В то время, как первоначальная система NeXT во многих местах использовала чистый C, новая версия определила два фреймворка: Foundation и AppKit. Foundation определил чистую функциональность, включая коллекцию классов, строк и другие вещи. AppKit содержал GUI-фреймворки. Оба были определены в спецификации OpenStep, которая была реализована Next, Sun, а позже — проектом GNU. После покупки NeXT, Apple переименовала реализацию OpenStep на «Cocoa». Так как OpenStep базировался на ранних разработках NeXT, название всех классов в OpenStep начинаются с «NS».
Достаточно интересна история поддержки Objective-C для GCC. Первоначальная реализация была написана в NeXT, но хранилась в отдельной библиотеке, которую пользователи могли подключить к их версии GCC. Не распространяя её вместе с GCC, NeXT надеялась обойти GPL. Этот трюк не сработал, и, в конечном счете, NeXT пришлось открыть код. Как бы то ни было, открытый код рантайм библиотеки не требовался, поэтому компилятор не работал на любой другой платформе. Ричард Столлман (Richard Stallman) написал новую версию, которая послужила заменой рантайму от NeXT. Она тоже, в свою очередь, была впоследствии заменена более новыми версиями, развитие которых пошло по разным путям.
Текущий код для Objective-C в GCC зависит от используемой версии. NeXT сохранила свою версию, которая представляет собой редко обновляемую кашу без иерархии, все находится в одном исходном файле и поддерживает только рантайм от NeXT. Основная ветка GCC включает совсем неподдерживаемый хлам, основанный на коде NeXT, но заполненный #ifdef-ами для рантайма GNU.
Как только GCC стал поддерживать Objective-C, начались попытки дублирования оболочки NeXT. Проект GNUstep в конечном счете выделился из них, реализуя большую часть OpenStep и часть дополнений Cocoa. Он также включает в себя замену построителя интерфейса от NeXT — GNU Opject Relational Modeler (GORM).
Немного синтаксиса
Синтаксические дополнения в Objective-C были спроектированы таким образом, чтобы выделить различную семантику объектно-ориентированного кода. В C++ вы можете сделать так:
Это может быть вызовом функции C, вызовом статической функции-члена текущего объекта в C++, переданной в качестве скрытого параметра или вызовом виртуальной функции, которая может быть реализована в дочернем классе, с текущим объектом в качестве скрытого параметра. С другой стороны, вы можете сделать так:
Это может быть вызовом указателя на функцию C, которая является полем структуры, вызовом статической функции-члена, определенной в классе, что вы воспринимаете как экземпляр или виртуальной функции, определенной в классе, с экземпляром которого вы действительно работаете.
В противоположность этому, вещи, которые выглядят как C, в Objective-C всегда являются C. Аналог вызова метода в Objective-C пришел из синтаксиса Smalltalk:
Квадратные скобки используются для выделения синтаксиса отправки сообщений, похожего на Smalltalk. Я вернусь к различию между отправкой сообщений и вызовом функций немного позже. Когда у вас есть метод, принимающий некоторые аргументы, у всех аргументов в Objective-C есть имена:
Это может выглядеть странным для тех, кто более близок к синтаксису C, но это позволяет тратить гораздо меньше времени, размышляя над порядком параметров. Это также делает код более удобным для чтения, если вы перед этим не смотрели класс, экземпляром которого является aDictionary, — вы знаете, что этот метод устанавливает объект и ключ, и вы знаете, что есть что.
После обработки, это превращается в нечто вроде следующего:
Я остановлюсь позже на том, что такое sel. Не смотря на то, что это работает, не совсем понятно, что произойдет — значение sel будет установлено инициализирующей процедурой рантайм библиотеки, когда модуль будет загружен. Семейство рантаймов NeXT/Apple объединяет два последних шага в следующее:
Это работает немного быстрее, но делает оптимизацию, вроде последующего добавления кода в будущем гораздо сложнее. Так как это все функции C, в них нет ничего особенного — вы можете вызывать их из своего собственного кода, если захотите.
Объектная модель
Термин «объектно-ориентированный» был введен Аланом Кеем (Alan Kay) в 1960-х. Он продолжил разработку языка Smalltalk вместе с командой в Xerox PARC, как языка объединяющего идеи этой модели программирования.
Основной идеей объектно-ориентированного программирования явились простые модели компьютеров, которые связаны друг с другом посредством обмена сообщений. Это очень отличается от подхода в C++, где присутствует нечто мало похожее на объектную ориентированность, а основной идеей является ассоциирование функций со структурами данных.
В C++ объекты — это структуры с набором связанных функций, которые являются либо статическими, либо виртуальными. Статическая функция — семантический эквивалент функции в C со скрытым первым аргументом, содержащим объект. Это крайне неэффективное расширение над C, так как следующие вещи эквивалентны:
Версия на C++ длиннее, но не дает каких-либо семантических отличий.
C++ также поддерживает кое-что более разумное в форме виртуальных функций, являющихся членами класса. Если функция в предыдущем примере является виртуальной, какая реальная функция будет вызвана, зависит от класса Object. Если она статическая, то это будет зависеть от того, объектом какого класса вызывающий считает Object.
В Objective-C не было добавлено эквивалента статической функции, являющейся членом класса. Если вы хотите такую функциональность, вы просто используете функции на C. Методы в Objective-C схожу с виртуальными методами в C++, но с несколькими важными отличиями. Первое — другой синтаксис:
Этот код отправляет сообщение с именем message объекту. Какой код будет вызван в качестве результата этого действия, полностью зависит от класса объекта. Вот одно очень важное отличие между этим и аналогом в C++: в C++ этот механизм до сих пор немного зависит от того, объектом какого класса вы считаете данный объект.
Предположим, что у вас есть иерархия из четырех классов. A — базовый класс, B и C — дочерние классы A, а D — дочерний класс B. Если каждый из них, кроме класса A, реализует виртуальную функцию doSomething() в C++, вы можете вызвать её, только используя шаблон. Рассмотрим следующую строку:
Если вы предполагаете, что Object — это объект класса B или D, и он действительно является объектом D, будет вызвана реализация, определенная в D. Если вы предполагаете, что это объект класса C, он вызывает реализацию из C. Если вы полагаете, что это объект класса A, вам необходимо будет попробовать провести два явных динамических приведения и посмотреть, какой сработает, или использовать шаблон.
Если у вас есть такой же порядок классов в Objective-C, с классами B, C и D, реализующих метод doSomething, вы можете попробовать следующее:
Если вы предполагаете, что данный объект является типом B, а на самом деле он типа C, метод doSomething все равно будет вызван. Если вы думаете, что это экземпляр класса A, при компиляции вы получите предупреждение о том, что экземпляры класса A не отвечают на сообщение doSomething. Если это действительно экземпляр классов B, C или D, то это сработает в рантайме, а если же он действительно экземпляр класса A, вы получите исключение в рантайме.
Множественное наследование недоступно в Objective-C, но здесь оно гораздо менее востребовано, чем в C++. В C++ метод поиска основан на цепочке наследования, так что вы можете использовать два класса иерархически, пока у них нет общего суперкласса. В Objective-C вы можете использовать любые два класса, как полностью взаимозаменяемые, если они отвечают на те же сообщения.
http://netsago.org/ru/docs/1/15/
Objective-C был спроектирован как минимальный набор расширений для C, предоставляющих семантику, схожую со Smalltalk. Он также включает в себя схожий со Smalltalk синтаксис, с помощью которого проще увидеть, какая часть кода является чистым C, а какая — содержит расширения Objective-C.
В отличие от C++, Objective-C — это расширенный набор C. Каждая программа, написанная на C, является корректной программой Objective-C. Все новые ключевые слова в Objective-C начинаются с символа @, который не является корректным идентификатором C.
Система рантайма
Первой реализацией Objective-C был препроцессор, который переводил Objective-C в C. Все динамические возможности языка поддерживались библиотекой рантайма. Современные компиляторы не использую C в качестве промежуточного звена, но они сохранили такой же порядок работы с библиотекой рантайма, предоставляющей динамические возможности.
В Objective-C объектами являются простые структуры на C, в которых первый элемент — указатель на другую структуру на C, представляющую класс. Что именно содержит эта структура класса — немного различается по времени запуска, но как минимум она содержит информацию для поиска методов, оболочку для переменных экземпляра («полей класса», говоря терминами C++) и некоторые другие метаданные.
Так как Objective-C является расширенным набором расширений над чистым C, а все возможности библиотеки рантайма (включая структуру классов) раскрываются в C, — это значит, что Objective-C полностью открывает объектную модель программисту. Нет никакой магии в Objective-C. Когда вы вызываете метод в C++, компилятор делает некоторые соответствующие действия с виртуальными таблицами, чтобы найти корректный код для запуска. В Objective-C, он делает что-то одно из следующих вариантов:
• Вызовет функцию C для поиска функции, реализующий метод (с рантайм GNU)
• Вызовет функцию C, которая ищет метод и вызывает его в одну операцию (с рантайм Apple).
Это значит, что вы можете писать чистый код на C, который взаимодействует с объектами Objective-C. Это также позволяет вам обходить динамическое поведение тогда, когда оно не нужно.
Немного истории
Objective-C был создан в ранних 1980-х Брэдом Коксом (Brad Cox), как набор минимальных расширений к C для просто поддержки объектов в стиле Smalltalk. Он вырос из проекта Object Oriented Pre-Compiler (OOPC), который имел очень похожую семантику, но действительно ужасный синтаксис.
Первоначальная реализация и торговая марка Objective-C была куплена компанией NeXT, и язык использовался повсеместно в их новой операционной системе. В NeXT на Objective-C сделали GUI тулкит и построитель интерфейсов. Это была первая коммерческая утилита для быстрой разработки приложений (Rapid Application Development, RAD). У неё также был хороший побочный эффект, заключавшийся в том, что построитель интерфейса не хранил одно только описание интерфейса — он хранил сериализованные объекты.
В ранних 1990-х NeXT и Sun сотрудничали в области разработки портативной версии программной среды NeXT. В то время, как первоначальная система NeXT во многих местах использовала чистый C, новая версия определила два фреймворка: Foundation и AppKit. Foundation определил чистую функциональность, включая коллекцию классов, строк и другие вещи. AppKit содержал GUI-фреймворки. Оба были определены в спецификации OpenStep, которая была реализована Next, Sun, а позже — проектом GNU. После покупки NeXT, Apple переименовала реализацию OpenStep на «Cocoa». Так как OpenStep базировался на ранних разработках NeXT, название всех классов в OpenStep начинаются с «NS».
Достаточно интересна история поддержки Objective-C для GCC. Первоначальная реализация была написана в NeXT, но хранилась в отдельной библиотеке, которую пользователи могли подключить к их версии GCC. Не распространяя её вместе с GCC, NeXT надеялась обойти GPL. Этот трюк не сработал, и, в конечном счете, NeXT пришлось открыть код. Как бы то ни было, открытый код рантайм библиотеки не требовался, поэтому компилятор не работал на любой другой платформе. Ричард Столлман (Richard Stallman) написал новую версию, которая послужила заменой рантайму от NeXT. Она тоже, в свою очередь, была впоследствии заменена более новыми версиями, развитие которых пошло по разным путям.
Текущий код для Objective-C в GCC зависит от используемой версии. NeXT сохранила свою версию, которая представляет собой редко обновляемую кашу без иерархии, все находится в одном исходном файле и поддерживает только рантайм от NeXT. Основная ветка GCC включает совсем неподдерживаемый хлам, основанный на коде NeXT, но заполненный #ifdef-ами для рантайма GNU.
Как только GCC стал поддерживать Objective-C, начались попытки дублирования оболочки NeXT. Проект GNUstep в конечном счете выделился из них, реализуя большую часть OpenStep и часть дополнений Cocoa. Он также включает в себя замену построителя интерфейса от NeXT — GNU Opject Relational Modeler (GORM).
Немного синтаксиса
Синтаксические дополнения в Objective-C были спроектированы таким образом, чтобы выделить различную семантику объектно-ориентированного кода. В C++ вы можете сделать так:
doSomething();
Это может быть вызовом функции C, вызовом статической функции-члена текущего объекта в C++, переданной в качестве скрытого параметра или вызовом виртуальной функции, которая может быть реализована в дочернем классе, с текущим объектом в качестве скрытого параметра. С другой стороны, вы можете сделать так:
a.doSomething();
Это может быть вызовом указателя на функцию C, которая является полем структуры, вызовом статической функции-члена, определенной в классе, что вы воспринимаете как экземпляр или виртуальной функции, определенной в классе, с экземпляром которого вы действительно работаете.
В противоположность этому, вещи, которые выглядят как C, в Objective-C всегда являются C. Аналог вызова метода в Objective-C пришел из синтаксиса Smalltalk:
[a doSomething];
Квадратные скобки используются для выделения синтаксиса отправки сообщений, похожего на Smalltalk. Я вернусь к различию между отправкой сообщений и вызовом функций немного позже. Когда у вас есть метод, принимающий некоторые аргументы, у всех аргументов в Objective-C есть имена:
[aDictionary setObject:@"a string" forKey:@"a key"];
Это может выглядеть странным для тех, кто более близок к синтаксису C, но это позволяет тратить гораздо меньше времени, размышляя над порядком параметров. Это также делает код более удобным для чтения, если вы перед этим не смотрели класс, экземпляром которого является aDictionary, — вы знаете, что этот метод устанавливает объект и ключ, и вы знаете, что есть что.
После обработки, это превращается в нечто вроде следующего:
SEL sel = sel_get_uid("setObject:forKey:");
IMP method = objc_msg_lookup(aDictionary->isa, sel);
method(aDictionary, sel, @"a string", @"a key");
IMP method = objc_msg_lookup(aDictionary->isa, sel);
method(aDictionary, sel, @"a string", @"a key");
Я остановлюсь позже на том, что такое sel. Не смотря на то, что это работает, не совсем понятно, что произойдет — значение sel будет установлено инициализирующей процедурой рантайм библиотеки, когда модуль будет загружен. Семейство рантаймов NeXT/Apple объединяет два последних шага в следующее:
objc_msgSend(aDictionary, sel, @"a string", @"a key");
Это работает немного быстрее, но делает оптимизацию, вроде последующего добавления кода в будущем гораздо сложнее. Так как это все функции C, в них нет ничего особенного — вы можете вызывать их из своего собственного кода, если захотите.
Объектная модель
Термин «объектно-ориентированный» был введен Аланом Кеем (Alan Kay) в 1960-х. Он продолжил разработку языка Smalltalk вместе с командой в Xerox PARC, как языка объединяющего идеи этой модели программирования.
Основной идеей объектно-ориентированного программирования явились простые модели компьютеров, которые связаны друг с другом посредством обмена сообщений. Это очень отличается от подхода в C++, где присутствует нечто мало похожее на объектную ориентированность, а основной идеей является ассоциирование функций со структурами данных.
В C++ объекты — это структуры с набором связанных функций, которые являются либо статическими, либо виртуальными. Статическая функция — семантический эквивалент функции в C со скрытым первым аргументом, содержащим объект. Это крайне неэффективное расширение над C, так как следующие вещи эквивалентны:
// C++
Object->function();
// C
function(Object);
Object->function();
// C
function(Object);
Версия на C++ длиннее, но не дает каких-либо семантических отличий.
C++ также поддерживает кое-что более разумное в форме виртуальных функций, являющихся членами класса. Если функция в предыдущем примере является виртуальной, какая реальная функция будет вызвана, зависит от класса Object. Если она статическая, то это будет зависеть от того, объектом какого класса вызывающий считает Object.
В Objective-C не было добавлено эквивалента статической функции, являющейся членом класса. Если вы хотите такую функциональность, вы просто используете функции на C. Методы в Objective-C схожу с виртуальными методами в C++, но с несколькими важными отличиями. Первое — другой синтаксис:
[object message];
Этот код отправляет сообщение с именем message объекту. Какой код будет вызван в качестве результата этого действия, полностью зависит от класса объекта. Вот одно очень важное отличие между этим и аналогом в C++: в C++ этот механизм до сих пор немного зависит от того, объектом какого класса вы считаете данный объект.
Предположим, что у вас есть иерархия из четырех классов. A — базовый класс, B и C — дочерние классы A, а D — дочерний класс B. Если каждый из них, кроме класса A, реализует виртуальную функцию doSomething() в C++, вы можете вызвать её, только используя шаблон. Рассмотрим следующую строку:
Object.doSomething();
Если вы предполагаете, что Object — это объект класса B или D, и он действительно является объектом D, будет вызвана реализация, определенная в D. Если вы предполагаете, что это объект класса C, он вызывает реализацию из C. Если вы полагаете, что это объект класса A, вам необходимо будет попробовать провести два явных динамических приведения и посмотреть, какой сработает, или использовать шаблон.
Если у вас есть такой же порядок классов в Objective-C, с классами B, C и D, реализующих метод doSomething, вы можете попробовать следующее:
[object doSomething];
Если вы предполагаете, что данный объект является типом B, а на самом деле он типа C, метод doSomething все равно будет вызван. Если вы думаете, что это экземпляр класса A, при компиляции вы получите предупреждение о том, что экземпляры класса A не отвечают на сообщение doSomething. Если это действительно экземпляр классов B, C или D, то это сработает в рантайме, а если же он действительно экземпляр класса A, вы получите исключение в рантайме.
Множественное наследование недоступно в Objective-C, но здесь оно гораздо менее востребовано, чем в C++. В C++ метод поиска основан на цепочке наследования, так что вы можете использовать два класса иерархически, пока у них нет общего суперкласса. В Objective-C вы можете использовать любые два класса, как полностью взаимозаменяемые, если они отвечают на те же сообщения.
- Просмотров: 1270
Версия для печати
#1
Vanger |
Vanger |
Дата публикации: 20 февраля 2009 23:22 | ICQ: --
цитировать
цитировать


за наводку на статью спасибо Lex Luthor'у