Расширение файлов objective c

Обновлено: 03.07.2024

Первоначально язык был разработан в начале 1980-х годов. Позже он был выбран в качестве основного языка, используемого NeXT для своей операционной системы NeXTSTEP , от которой произошли macOS и iOS. [4] Переносимые программы Objective-C, которые не используют библиотеки Apple или те, которые используют части, которые могут быть перенесены или переопределены для других систем, также могут быть скомпилированы для любой системы, поддерживаемой GNU Compiler Collection (GCC) или Clang .

СОДЕРЖАНИЕ

Objective-C был создан в первую очередь Брэдом Коксом и Томом Лавом в начале 1980-х годов в их компании Productivity Products International (PPI) . [5]

Приводя к созданию своей компании, как были введены в Smalltalk в то время как в ITT Corporation Программирование Technology Center «s в 1981. Самые ранние работы на Objective-C прослеживает около того времени. [6] Кокса заинтриговали проблемы истинного повторного использования в разработке и программировании программного обеспечения. Он понял, что такой язык, как Smalltalk, будет неоценимым средством создания сред разработки для системных разработчиков в ITT. Однако он и Том Лав также признали, что обратная совместимость с C была критически важна в среде инженеров ITT в области телекоммуникаций. [7]

Кокс начал писать препроцессор для C, чтобы добавить некоторые возможности Smalltalk. Вскоре у него была рабочая реализация объектно-ориентированного расширения языка C , которую он назвал «OOPC» для объектно-ориентированного предварительного компилятора. [8] Лав был нанят компанией Schlumberger Research в 1982 году и имел возможность приобрести первую коммерческую копию Smalltalk-80, которая в дальнейшем повлияла на развитие их детища. Чтобы продемонстрировать реальный прогресс, Кокс показал, что создание взаимозаменяемых программных компонентовдействительно требовалось лишь несколько практических изменений существующих инструментов. В частности, они должны были обеспечивать гибкую поддержку объектов, поставляться с полезным набором библиотек и позволять объединять код (и любые ресурсы, необходимые для кода) в один кроссплатформенный формат.

Лав и Кокс в конечном итоге создали PPI для коммерциализации своего продукта, который объединил компилятор Objective-C с библиотеками классов. В 1986 году Кокс опубликовал основное описание Objective-C в его первоначальной форме в книге « Объектно-ориентированное программирование, эволюционный подход» . Хотя он был осторожен, чтобы указать, что проблема повторного использования - это нечто большее, чем просто то, что предоставляет Objective-C, язык часто обнаруживал, что сравнивает функцию за функцией с другими языками.

В 1988 году NeXT лицензировал Objective-C у StepStone (новое название PPI, владельца торговой марки Objective-C) и расширил компилятор GCC для поддержки Objective-C. NeXT разработала библиотеки AppKit и Foundation Kit, на которых были основаны пользовательский интерфейс NeXTSTEP и Interface Builder. Хотя рабочие станции NeXT не смогли оказать большого влияния на рынок, инструменты получили широкое признание в отрасли. Это привело к тому, что NeXT отказалась от производства оборудования и сосредоточилась на программных инструментах, продав NeXTSTEP (и OpenStep) в качестве платформы для индивидуального программирования.

Чтобы обойти условия GPL , NeXT изначально намеревался поставлять интерфейс Objective-C отдельно, позволяя пользователю связать его с GCC для создания исполняемого файла компилятора. Хотя первоначально этот план был принят Ричардом М. Столлманом , этот план был отклонен после того, как Столмен проконсультировался с юристами GNU, и NeXT согласился сделать Objective-C частью GCC. [9]

Работой по расширению GCC руководил Стив Нарофф, который присоединился к NeXT из StepStone. Изменения компилятора были сделаны доступными в соответствии с условиями лицензии GPL , но библиотеки времени выполнения - нет, что сделало вклад с открытым исходным кодом непригодным для широкой публики. Это привело к тому, что другие стороны разработали такие библиотеки времени выполнения под лицензией с открытым исходным кодом. Позже Стив Нарофф также был основным сотрудником Apple по созданию интерфейса Objective-C для Clang .

Проект GNU начал работу над своей свободной программной реализацией Cocoa под названием GNUstep , основанной на стандарте OpenStep . [10] Деннис Глаттинг написал первую среду выполнения GNU Objective-C в 1992 году. Среда выполнения GNU Objective-C, которая используется с 1993 года, была разработана Крестеном Крабом Торупом, когда он был студентом университета в Дании . [ необходима цитата ] Торуп также работал в NeXT с 1993 по 1996 год. [11]

После приобретения NeXT в 1996 году, Apple Computer используется OpenStep в тогдашнем новой операционной системе Mac OS X . Это включало Objective-C, инструмент разработчика NeXT на основе Objective-C, Project Builder и его инструмент дизайна интерфейса Interface Builder , которые теперь объединены в одно приложение Xcode . Большая часть текущего API Cocoa от Apple основана на интерфейсных объектах OpenStep и является наиболее важной средой Objective-C, используемой для активной разработки.

На WWDC 2014 Apple представила новый язык Swift , который был охарактеризован как «Objective-C без C».

Objective-C представляет собой тонкий слой поверх C и является «строгим надмножеством » C, что означает, что можно скомпилировать любую программу C с помощью компилятора Objective-C и свободно включать код языка C в класс Objective-C. [12] [13] [14] [15] [16] [17]

В Objective-C это записывается следующим образом:

Objective-C требует, чтобы интерфейс и реализация класса находились в отдельно объявленных блоках кода. По соглашению разработчики помещают интерфейс в файл заголовка, а реализацию - в файл кода. Файлы заголовков, обычно с суффиксом .h, похожи на файлы заголовков C, в то время как файлы реализации (методов), обычно с суффиксом .m, могут быть очень похожи на файлы кода C.

Это аналогично объявлениям классов, используемым в других объектно-ориентированных языках, таких как C ++ или Python.

Объявление интерфейса принимает форму:

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

Приведенный выше код примерно эквивалентен следующему интерфейсу C ++ :

Обратите внимание, что instanceMethod2With2Parameters: param2_callName: демонстрирует чередование сегментов селектора с выражениями аргументов, для которых нет прямого эквивалента в C / C ++.

Типы возврата могут быть любым стандартным типом C , указателем на универсальный объект Objective-C, указателем на объект определенного типа, например NSArray *, NSImage * или NSString *, или указателем на класс, к которому принадлежит метод. (тип экземпляра). Тип возврата по умолчанию - это общий идентификатор типа Objective-C .

Аргументы метода начинаются с имени, обозначающего аргумент, являющегося частью имени метода, за которым следует двоеточие, за которым следует ожидаемый тип аргумента в круглых скобках и имя аргумента. Этикетку можно не указывать.

Производным от определения интерфейса является категория , которая позволяет добавлять методы к существующим классам. [22]

Методы написаны с использованием деклараций их интерфейсов. Сравнение Objective-C и C:

Синтаксис допускает псевдонимы аргументов .

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

Создание экземпляра со стандартным инициализатором без параметров:

Создание экземпляра с помощью настраиваемого инициализатора:

Кроме того, некоторые классы реализуют инициализаторы методов класса. Мол +new , они сочетаются +alloc и -init , но в отличие от +new них, возвращают автоматически выпущенный экземпляр. Некоторые инициализаторы методов класса принимают параметры:

Обратите внимание на id возвращаемый тип в приведенном выше примере . Этот тип означает «указатель на любой объект» в Objective-C (см. Раздел « Динамическая типизация »).

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

Если класс имеет более одного метода инициализации, только один из них («назначенный инициализатор») должен следовать этому шаблону; другие должны вызывать назначенный инициализатор вместо инициализатора суперкласса.

В других языках программирования они называются «интерфейсами».

Неформальный протокол - это список методов, которые класс может реализовать. Это указано в документации, так как в языке его нет. Неформальные протоколы реализованы как категория (см. Ниже) в NSObject и часто включают необязательные методы, которые, если они реализованы, могут изменить поведение класса. Например, у класса текстового поля может быть делегат , реализующий неформальный протокол с дополнительным методом для выполнения автозаполнения вводимого пользователем текста. Текстовое поле определяет, реализует ли делегат этот метод (через отражение ), и, если да, вызывает метод делегата для поддержки функции автозаполнения.

Чтобы было сказано, что он соответствует этому протоколу, должен быть объявлен класс для реализации этого протокола. Это можно обнаружить во время выполнения. Формальные протоколы не могут предоставить никаких реализаций; они просто гарантируют вызывающим абонентам, что классы, соответствующие протоколу, предоставят реализации. В библиотеке NeXT / Apple протоколы часто используются системой распределенных объектов для представления возможностей выполнения объекта в удаленной системе.

означает, что существует абстрактная идея блокировки. Заявив в определении класса, что протокол реализован,

экземпляры NSLock утверждают, что они предоставят реализацию для двух методов экземпляра.

Информация о статической типизации также может быть добавлена ​​к переменным. Затем эта информация проверяется во время компиляции. В следующих четырех утверждениях предоставляется все более конкретная информация о типе. Операторы эквивалентны во время выполнения, но дополнительная информация позволяет компилятору предупреждать программиста, если переданный аргумент не соответствует указанному типу.

В приведенном выше утверждении foo может относиться к любому классу.

В приведенном выше утверждении foo может быть экземпляром любого класса, который соответствует NSCopying протоколу.

В приведенном выше утверждении foo должен быть экземпляром класса NSNumber .

В приведенном выше заявлении foo должен быть экземпляром класса NSNumber и соответствовать NSCopying протоколу.

Среда выполнения Objective-C определяет пару методов в Object

Вот пример программы, демонстрирующей основы пересылки.

При компиляции с использованием gcc компилятор сообщает:

При разработке Objective-C одной из основных проблем была возможность сопровождения больших кодовых баз. Опыт мира структурированного программирования показал, что один из основных способов улучшить код - разбить его на более мелкие части. Objective-C заимствовал и расширил концепцию категорий из реализаций Smalltalk, чтобы помочь в этом процессе. [25]

Кроме того, методы в категории добавляются к классу во время выполнения . Таким образом, категории позволяют программисту добавлять методы к существующему классу - открытому классу - без необходимости перекомпилировать этот класс или даже иметь доступ к его исходному коду. Например, если система не содержит средства проверки орфографии в своей реализации String, ее можно добавить без изменения исходного кода String.

При запуске программы методы внутри категорий становятся неотличимы от методов класса. Категория имеет полный доступ ко всем переменным экземпляра в классе, включая частные переменные.

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

Другие языки пытались добавить эту функцию разными способами. TOM продвинул систему Objective-C на шаг вперед и позволил также добавлять переменные. В других языках вместо этого использовались решения на основе прототипов , наиболее заметным из которых является Self .

Logtalk реализует концепцию категорий (как первоклассных сущностей), которая включает в себя функциональность категорий Objective-C (категории Logtalk также могут использоваться как детализированные единицы композиции при определении, например, новых классов или прототипов; в частности, категория Logtalk может быть практически импортируется любым количеством классов и прототипов).


У любого желающего писать программы для продукции фирмы Apple в жизни наступает такой момент, когда ему приходиться изучить новый язык программирования — Objective-C. Когда-то этот счастливый момент постиг и меня. А чтобы лучше запомнить основные особенности этого языка, решил законспектировать свои мысли во время осмысления его документации, которыми и делюсь с вами.

Банальная теория возникновения ООП

Проблема повторного использования написанного кода и его переносимость постоянно заставляет программистов искать все новые пути его упорядочивания, структуризации и абстрагирования. Для решения этих проблем создаются новые парадигмы программирования, шаблоны проектирования, новые языки, компиляторы и стандартные библиотеки к ним, программные платформы и фреймворки. Так образовались парадигма подпрограмм (процедур), реализуемая при помощи процессорных команд CALL\RET и стека (по сути, перенос потока выполнения по адресу произвольной, а не следующей за текущей команды, с последующим возвратом). Затем, парадигма модулей (каждый файл – отдельная единица трансляции), породившая двухэтапную трансляцию: компиляция модулей, а затем их компоновка (статическая или динамическая) в исполняемый модуль.


Класс обычно оформляется как определенный программистом тип, основанный на встроенных (языковых) типах данных и\или других классах. Для языка С, не поддерживающего объектно-ориентированную парадигму, это может быть структура (struct). А набор подпрограмм реализуется как обычные функции, обязательно принимающие как минимум один параметр — указатель на набор данных, подлежащих обработке.

Основным преимуществом объектно-ориентированного подхода стала возможность создавать новые классы на основе уже написанных (добавлять инварианты и методы, переопределять методы, использовать определенные в базовом классе методы как свои), названное наследованием.


Набор методов представляет собой интерфейс для взаимодействия с инвариантами. Невозможность непосредственной модификации данных класса (без задействования его интерфейса) отражает принцип инкапсуляции. На рисунке показан класс и его объекты. Имеется инвариант x типа float и к нему интерфейс (метод) doubleX, возвращающий значение инварианта.

Первым языком с поддержкой объектно-ориентированного подхода стал Simula67. Затем появился Smalltalk. А в 80х начал оформляться С++ — основной язык современного системного программирования. Его расширение и усовершенствование в 90х породило ряд парадигм и шаблонов проектирования, и оказало необратимое влияние на современное видение объектно-ориентированного подхода, в том числе, и на язык Objective-C.

Чуть-чуть истории

Objective-C возник в 80-x как модификация С в сторону Smalltalk. Причем модификация эта состояла в добавлении новых синтаксических конструкций и специальном препроцессоре для них (который, проходя по коду преобразовывал их в обычные вызовы функций С), а также библиотеке времени выполнения (эти вызовы обрабатывающей). Таким образом, изначально Objective-C воспринимался как надстройка над C. В каком-то смысле это так и до сих пор: можно написать программу на чистом С, а после добавить к ней немного конструкций из Objective-C (при необходимости), или же наоборот, свободно пользоваться С в программах на Objective-C. Кроме того, все это касается и программ на С++. В 1988 NeXT (а в последствии Apple) лицензировала Objective-C и написала для него компилятор и стандартную библиотеку (по сути SDK). В 1992 к усовершенствованию языка и компилятора подключились разработчики проекта GNU в рамках проекта OpenStep. С тех пор GCC поддерживает Objective-C. После покупки NeXT, Apple взяля их SDK (компилятор, библиотеки, IDE) за основу для своих дальнейших разработок. IDE для кода назвали Xcode, а для GUI – Interface Builder. Фреймворк Cocoa для GUI разработок (и не только) на сегодня является наиболее значимой средой разработки программ на Objective-C.

Особенности Objective-C

  • interface Начинает объявление класса или категории (категория – расширение класса дополнительными методами без наследования)
  • @implementation Начинает определение класса или категории
  • @protocol Начинает объявление протокола (аналог класса С++, состоящего из чисто виртуальных функций)
  • end Завершает объявление\определение любого класса, категории или протокола
  • @private Ограничивает область видимости инвариантов класса методами класса (аналогично С++)
  • protected Стоит по умолчанию. Ограничивает область видимости инвариантов класса методами класса и методами производных классов (аналогично С++)
  • @public Удаляет ограничения на облать видимости (аналогично С++)
  • try Определяет блок с возможной генерацией исключений (аналогично С++)
  • @throw Генерирует объект-исключение (аналогично С++)
  • catch () Обрабатывает исключение, сгенерированное в предшествующем блоке try (аналогично С++)
  • finally Определяет блок после блока try, в который предается куправление независимо от того, было или нет сгенерировано исключение
  • @class Сокращенная форма объявления класса (только имя (аналогично С++))
  • selector(method_name) Возвращает скомпилированный селектор для имени метода method_name
  • @protocol(protocol_name) Ворзвращает экземпляр класса-протокола с именем protocol_name
  • @encode(type_spec) Инициализирует строку символов, которая будет использована для шифрования данных типа type_spec
  • @synchronized() Определяет блок кода, выполняющегося только одной нитью в любой определенный момент времени



Перед каждым параметром необходимо ставить двоеточие. Сколько двоеточий – столько и параметров. Имя метода может продолжаться после каждого такого двоеточия-параметра:


Методы с неограниченным количством аргументов вызываюся следующим синтаксисом:


Тип SEL, по сути, определен как char const *, но лучше воспринимать его как int, поскольку во время выполнения все селекторы индексируются целыми значениями согласно глобальной таблице селекторов.



При такой схеме вызов, например:


Так как в глобальной таблице селекторов 12 соответствует строке “addObject:”. Далее функция objc_msgSend() выполняет поиск по списку селекторов объекта receiver и, найдя его (пусть это объект класса NSArray, который реализовал метод с селектором 12), производит вызов типа:

Объявление метода

Интересно отметить, что прототип метода addObject из предыдущего раздела в объявлении класса выглядел так:

Если в начале прототипа метода поставить знак плюс (“+”), то такой метод будет считаться методом класса, и, естественно, не будет принимать неявный параметр self (это аналогично объявлению static-метода в С++). А без инварианта isa объекта, на который указывает self, указатель super работать, конечно, тоже не будет.
Таким образом, прототип любого метода объявляется так:


Если метод возвращает некий объект (тип id) или класс (тип Class), можно воспользоваться вложенным синтаксисом вызова:

Объявление класса

Объявим простой класс комплексного числа в файле Complex.h:

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


После создания объекта им можно смело пользоваться:


Некоторые классы обладают методом для быстрого (в один этап) создания собственных экземпляров. Такие методы являются методами класса, возвращают указатель на объект своего класса и их имя обычно начинается с названия самого класса. Например метод:


Возвращает уже готовую строку, инициализированную соответствующей сторокой с завершающим нулем, без вызовов alloc и init:

Время жизни объекта


Реализация init очень важна. Это конструктор класса. Конструкторы отличаются тем, что возвращаеют id и их названия всегда начинается со слова init, а конструктор по умолчанию – это и есть просто init. Схема любого конструктора примерно следующая:


Вот типичный специализированный (не по умолчанию) конструктор для класса с двумя членами типа некоторого класса и одним целочисленным инвариантом:


Реализация release и retain для NSObject идеологически примерно следующая, и ее не нужно переопределять в производных классах, в силу отсутствия доступа к инварианту счетчика ссылок:

Методы доступа

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


Так как инвариант _re относится ко встроенному типу, никаких сложностей с изменением его значения не возникает. Но если инвариант – объект некоторого класса – то простым присваиванием не обойтись, ведь надо учитывать счетчики ссылок. Для решения этой проблемы применяются следующие три метода:


Вариант №3 не очень удачный потому, что засоряет текущий самовыгружаемый пул, а обычно это не очень желательно (см. следующий раздел).
Метод доступа для чтения значения инварианта всегда очень прост:

Самовыгружаемый пул в нитях программы

Теперь попробуем вернуть из метода созданный внутри него объект:


Строка формата соответствует стандарту языка С. Но если в ней необходимо указать тип id, то используется спецификатор формата %@. Каким образом метод, разбирающий формат, понимает какие символы подставить вместь id? Он просто подставит то, что вернет метод описания description данного объекта. Этот метод изначально объявлен для класса NSObject. NSString переопределил его на вывод своего строкового содержания. Переопределив его, любой объект может представлять свое строковое содержание. Например, так это может сделать класс комплексного числа с двумя инвариантами типа double:

Любая нить в программе, использующей Cocoa, должна создавать объект класса NSAutoreleasePool в самом начале (прежде создания других объектов), и в самом конце его уничтожать (после уничтожения всех других объектов). Функция main(), являющаяся главной нитью любой программы на Objective-C, при использовании фреймворка Cocoa должна всегда выглядеть вот так:


А корректный метод sayHelloToName:withSurname: теперь будет выглядеть вот так:


К слову, метод drain самовыгружаемого пула аналогичен release с той лишь разницей, что, кроме освобождения себя самомго и всех содержащихся объектов, еще дает подсказку сборщику мусора вступить в игру. Однако, это актуально только для Mac OS 10.4 и выше, так как на iOS сборки мусора нет.

Определение класса

Теперь рассмотрим файл Complex.m с определением методов класса Complex:

Категории и расширения

Если к уже написанному (а, возможно, и откомпилированному) классу нужно добавить\переопределить некоторые методы без наследования – категории позволяют это сделать без особых усилий:


А пользоваться этим можно вот так:


Расширения несут добрую службу как безымянные категории:

Протоколы

Протокол Objective-C – это формализованное объявление группы методов, которые, по желанию, может реализовать любой класс (аналог класса в С++, где все методы объявлены со спецификатором virtual … = 0). В версии языка 2.0 методы протокола могут быть требуемыми (спецификатор @required, он считается умалчиваемым) и выборочными (спецификатор @optional). Если какой либо класс реализовал требуемые методы протокола, то он называется классом, поддерживающим данный протокол. Протокол, и класс, его поддерживающий, объявляются вот так:


Кроме того, один класс может удовлетворять нескольким протоколам. Для этого их можно перечислить через запятую в угловых скобках в объявлении класса.


А чтобы объявить объект неизвестного класса (id), соответствующий некоторому протоколу, пишут так:

Исключения

Есть два основных подхода к обработке ошибок: глобальная статусная переменная, значение которой информирует об успешности выполнения предыдущей операции, и генерация исключений. Суть обоих в том, что код, в котором произошла ошибка, надеется, что решить ее сможет вызвавший его код, поэтому возвращает управление ему, сообщая о произошедшей ситуации как можно более подробно. Objective-C поддерживает оба эти подхода.

Исключение – это объект некоторого класса. Он (даже своим типом) несет в себе некоторую информацию о произошедшей ситуации. Для удобства в Cocoa имеется класс NSException, который можно инициализировать двумя объектами NSString и одним объектом произвольного класса (тип id):


Сгенерировать исключение и, тем самым, запустить механизм раскрутки стека вызовов, можно с помощью оператора @throw. Чтобы перхватить сгенерированное исключение, участок кода, где возможна его генерация, необходимо заключить в специальный блок с заглавием try (такие блоки могут быть вложенными). А затем, после этого блока, поставить блок с заглавием catch(), где в круглых скобках указать тип предполагаемого исключения. Блоков catch() после блока try может быть несколько. После генерации исключения управление, раскручивая стек, выходит из блока try и, проверяя по очереди все блоки catch(), попадает именно в тот блок catch(), в фигурных скобках которого стоит такой тип, к которому тип исключения приводится неявно (точное совпадение, указатель на базовый класс или id). Если исключение по типу не совпало ни с одним блоком catch(), управление продолжает раскрутку стека. Если после блока с заглавием try стоит блок с заглавием finally, то управление передастся ему независимо от того, произошло ли в блоке try исключение (и обработан какой-нибудь блок catch()), или выполнилась его последняя инструкция. Ниже приведен пример работы с объектом класса Cup в методе fill которого происходит исключение:


В блоке finally удобно освобождать ресурсы, выделенные в блоке try, но не освобожденные по причине сгенерированного исключения.

Свойства

Для версии Objective-C 2.0 нашa реализация класса Complex явно избыточна: в ней слишком много методов доступа и их определение – сплошная рутина. Перепишем его с использованием свойств:

Если же вы не программировали раньше в языках ООП, тогда вам нужно усвоить некоторые базовые концепции, перед тем как приступить к разработке приложений. Использование объектов ООП является фундаментальным в работе iPhone, и понимание их использования и взаимодействия между ними важны для программирования. Для того чтобы понять концепции ООП, изучите Object-Oriented Programming with Objective-C. Вы также можете изучить Cocoa Fundamentals Guide чтобы узнать о применении концепций ООП в Cocoa. Для более детального изучения синтаксиса языка Objective-C вы можете изучить The Objective-C 2.0 Programming Language.

Objective-C: Как подмножество C

Таблица 1 Расширения файлов для кода Objective-C

Расширение Тип исходников
.h Заголовочные (header) файлы. Cодержат классы, типы, функции и объявление констант.
.m Файлы кода (source). Это типовое расширение файлов для исходного кода как Objective-C, так и C-кода.
.mm Файлы кода (source). Файлы с таким расширением обычно содержат код C++ в дополнении к коду Objective-C и C. Это расширение используется только, если вы фактически обращаетесь к классам или особенностям C++ из вашего Objective-C кода.

Строковые переменные

Как в подмножестве C, в Objective-C поддерживается те же самые соглашения для того, чтобы определить строки как и в C. Другими словами, единичные символы заключаются в одинарные кавычки и строки заключаются в двойные кавычки. Однако, большинство структур (frameworks) в Objective-C не часто используют стиль описания строк как в C. Вместо этого, большинство frameworks раздают строки в объектах типа NSString.

Классы

Определение класса в Objective-C требует две различные части: интерфейс (interface) и реализация (implementation). Интерфейс определяет структуру экземпляров класса, а также instance-переменные и методы, связанные с классом. Реализация содержит код для описания методов класса. Иллюстрация 1 показывает синтаксис, объявляющий класс по имени MyClass, который наследуется из базового класса NSObject. Декларация класса всегда начинается с директивы компилятора @interface и заканчивается директивой @end. После имени класса следует имя родительского класса, отделенное двоеточием. Instance-переменные класса объявляются в блоке кода, который заключен в скобки (< и >). После блока описания instance-переменных следует список методов, объявленных классом. После каждого объявления instance-переменной или метода ставится точка с запятой.

Иллюстрация 1 Определение класса

Листинг 1 Реализация класса

Когда вы храните объекты внутри переменных, вы так или иначе используете ссылочный тип (pointer type). Objective-C поддерживает как явное, так и неявное объявление типов переменных, содержащих объекты. Явно объявленные указатели включают название класса в объявлении типа переменной. Неявно объявленный тип указателя, в отличии от явного, использует тип id для объектов. Неявно объявленный тип указателя чаще всего используется для таких вещей, как коллекционные классы, где может быть неизвестным точный тип объектов коллекции. Если вы привыкли использовать языки, где используется только явное объявление типов, тогда наверное вы будете думать, что использование неявного объявления типа переменных вызовет кучу проблем. Однако, на самом деле, это обеспечивают огромную гибкость и намного большую динамичность в программах Objective-C.

Вот пример обоих типов объявления переменных для класса Myclass:

Методы

Иллюстрация 2 Синтаксис объявления метода

Свойства

Проще говоря, свойства позволяют сократить количество избыточного кода, который вы будете писать. Поскольку большинство методов доступа осуществляется именно подобным способом, свойства избавляют нас от необходимости явно устанавливать методы getter и setter для каждой переменной экземпляра, указанной в классе. Вместо этого вы описываете поведение, которое вы хотите использовать для объявления свойств и затем синтезируете фактические getter и setter методы, основанные на этом объявлении, прямо во время компиляции.

Включаете объявления свойств с объявлениями метода в вашем интерфейс-классе. Для объявления свойств используют директиву @property, сопровождаемую информацией о типе и имени свойства. Вы можете также описывать свойства с собственными параметрами, которые определяют, как методы доступа должны себя вести. Ниже приведены примеры объявления нескольких простых свойств:

Хотя имена объекта и свойства в предыдущем примере надуманы, они все же демонстрируют гибкость свойств. Синтаксис с применением точки фактически маскирует соответствующий набор вызовов метода. Каждое доступное для чтения свойство поддерживается методом с таким же именем, что и само свойство. Каждое доступное для записи свойство поддерживается дополнительным методом формы setPropertyName: , где имя свойства начинается с большой буквы. (Эти методы являются фактической реализация свойств и являются причиной, по которой вы можете включать объявления свойства для атрибутов вашего класса не поддерживаемых переменными экземпляра класса). Давайте перепишем предыдущий пример с использованием методов вместо свойств:

Протоколы и Делегаты

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

Например, класс UIApplication реализует нужное нам поведение приложения. Вместо того, чтобы заставлять подкласс UIApplication получать простые запросы о текущем состоянии приложения, класс UIApplication передает эти запросы назначенному объекту-делегату, вызывая при этом определенные методы. Тоесть, объект, который реализует методы протокола UIApplicationDelegate, может получать эти запросы и выдавать на них соответствующий ответ.

Объявление протокола похоже на интерфейс класса, за исключением того, что у протоколов нет родительского класса, и они не определяют переменные экземпляра. Следующий пример показывает простое объявление протокола с одним методом:

объектно-ориентированный Apple, построенный на основе языка Mac OS X (Cocoa) и OpenStep. Также язык используется для Cocoa Touch).

Содержание

История [ ]

В начале 1980-х годов было популярно Clang и GCC (под управлением MinGW или Синтаксис языка [ ]

В языке Objective-C для обозначения объектов используется специальный тип id (это аналог типа Object в

Язык Objective-C позволяет снабжать метками каждый аргумент, что заметно повышает читаемость кода и снижает вероятность передачи неправильного параметра. Именно такой стиль принят большинством разработчиков.

Как уже говорилось, в Objective-C классы сами являются объектами. Основной задачей таких объектов (называемых class objects) является создание экземпляров данного класса (это очень похоже на паттерн Abstract Factory ).

В языке Objective-C нет встроенного типа для булевских величин, поэтому обычно такой тип вводится искусственно. Далее для логических величин будет использоваться тип BOOL с возможными значениями YES и NO (как это делается в операционных системах NextStep, Mac OS X).

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

Имена всех этих классов начинаются с префикса NS, обозначающего свою принадлежность к операционной системе NextStep. Сейчас они входят в библиотеку Foundation, на которой строятся приложения для OS X и iOS.

Компилятор поддерживает данный тип, автоматически переводя конструкции вида @"my string" в

Для доступа к ней извне лучше всего воспользоваться

В скобках перечисляются атрибуты доступа к instance-переменной. Атрибуты разделяются на 3 основные группы.

Имена акцессора и мутатора

  • getter=getterName, используется для задания имени функции, используемой для извлечения значения instance-переменной.
  • setter=setterName, используется для задания имени функции, используемой для установки значения instance-переменной.

Ограничение чтения/записи

Эти атрибуты взаимоисключают друг друга. И последняя группа атрибуты мутатора.

Автоматически созданный код — не всегда подходящее решение и может потребоваться создание методов доступа к instance-переменным вручную.

Язык часто критикуют за перегруженный, по сравнению с другими языками, синтаксис. Однако при этом нередко отмечается его более высокая читабельность.

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

Все ключевые слова языка Objective-C, отсутствующие в С, начинаются с символа @.

Ниже приводится общая структура описания нового класса:

Описание переменных ничем не отличается от описания переменных в структурах в языке С:

Описания же методов заметно отличаются от принятых в C++ и очень сильно похожи на описания методов в языке Smalltalk.

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

Обратите внимание, что имя метода может совпадать с именем instance-переменной данного класса (например, width и height).

Тип возвращаемого методом значения указывается в круглых скобках сразу же после знака плюс или минус (но перед названием метода). Если тип не указан, то считается, что возвращается значение типа id.

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

Метод, принимающий произвольное количество параметров, может быть описан следующим образом:

Реализация методов класса выглядит следующим образом:

Ниже приводится пример реализации методов класса Rect, описанного выше.

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

При этом к public переменным класса можно обращаться непосредственно, используя оператор -> (например objPtr -> fieldName).

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

Тип селектора обозначается как SEL и существует ряд функций и конструкций, позволяющих осуществлять преобразование имени в селектор и обратно.

Для получения селектора по строке символов (на этапе выполнения) и перевода селектора в строку служат функции NSSelectorFromString и NSStringFromSelector:

Обратите внимание, что методы performSelector: всегда возвращают значение типа id.

С другой стороны также можно легко получить соответствующий class object по имени класса:

В языке Objective-C можно по селектору метода получить адрес реализующей его функции (именно как функции языка С).

Протоколы [ ]

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

Протоколы удобны тем, что позволяют выделять общие черты у разнородных объектов и передавать информацию об объектах заранее неизвестных классов.

Простейшее описание протокола выглядит следующим образом:

Так протокол Serializable может быть описан следующим образом:

Протокол может быть унаследован от произвольного количества других протоколов:

Точно также можно при описании класса задать не только родительский класс, но и набор протоколов:

Кроме того, имя протокола (протоколов) можно использовать при описании переменных для явного указания компилятору о поддержке соответствующими объектами протокола (протоколов).

Так, если переменная myObject содержит указатель на объект заранее неизвестного класса, но при этом удовлетворяющий протоколам Serializable и Drawable, то её можно описать следующим образом:

Точно так же, если заранее известно, что myObject будет содержать указатель на объект, унаследованный от класса Shape и поддерживающего протокол Serializable, то эту переменную можно описать следующим образом:

Как и классы, все протоколы в Objective-C представлены при помощи объектов (класса Protocol):

Для предварительного объявления протоколов можно использовать следующую конструкцию:

Эта конструкция сообщает компилятору о том, что MyProto, Serializable и Drawable являются именами протоколов, которые будут определены позже.

Обработка исключений [ ]

В языке Objective-C поддерживается обработка исключений, очень похожая на используемую в языках C++ и Java.

Для этого служат директивы @try, @catch, @finally и @throw.

Для запуска исключения используется

Внутри @catch-блоков директива @throw может использоваться без параметра для повторного запуска обрабатываемого исключения (rethrowing exception).

Синхронизация [ ]

Язык Objective-C поддерживает синхронизацию для

В качестве мьютекса (т. е. параметра инструкции @synchronized) рекомендуется указывать объект, недоступный извне, поскольку это может привести к Создание и уничтожение объектов [ ]

Реально используемой и наиболее широко распространенной схемой создания и уничтожения объектов в Objective-C является используемая в операционных системах NextStep и Mac OS X, которая и будет описана ниже.

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

Это связано с тем, что для ряда классов метод init может вернуть совсем другой указатель (а не self).

Простейшими примерами того, когда может возникать подобная ситуация, являются

В ряде случаев оказывается удобным совместить выделение памяти и инициализацию объекта в один метод (класса), например в классе NSString есть ряд методов класса, возвращающих уже готовый (проинициализированный) объект:

Управление памятью [ ]

Базовые принципы [ ]

Управление памятью в Objective-C базируется на принципе «владения объектом». Основные правила управления памятью в Objective-C можно записать так:

  • Для получения объекта во владение необходимо вызвать метод, содержащий в названии «alloc», «new» либо «copy». Например, alloc, newObject, mutableCopy.
  • Для освобождения объекта, который был получен при помощи перечисленных выше функций, необходимо вызвать функцию «release» либо «autorelease». Во всех остальных случаях освобождение объекта не требуется.
  • Если полученный объект должен быть сохранен, необходимо либо стать его владельцем (вызвав retain), либо создать его копию (вызов, содержащий в названии «copy»).

Данные правила базируются на соглашении по именованию в Objective-C и, в то же время, сами являются основой этого соглашения.

Базовые принципы на практике [ ]

Предположим, в программе существует класс Company, у которого есть метод workers.

Рассмотрим небольшой пример использования такого класса:

Так как объект класса Company создается явно, он должен быть удален по окончании использования ([company release]). В то же время, название метода workers не говорит о том, кто должен удалять массив. В такой ситуации считается, что списком работников управляет объект Компания и его удалять не требуется.

Многие классы позволяют совместить создание объекта с его инициализацией при помощи методов, называемых convenience конструкторы; такие методы обычно называются +className… Можно предположить, что вызывающая сторона ответственна за управление временем жизни объекта, но подобное поведение противоречило бы соглашению по именованию в Objective-C.

В приведенном коде вызов [company release] недопустим, так как в данном случае управление временем жизни объекта должно осуществляться при помощи autorelease пула.

Ниже приводится пример корректной реализации метода company:

Вернемся к методу workers класса Company. Так как возвращается массив, временем жизни которого вызывающая сторона не управляет, реализация метода workers будет выглядеть приблизительно так:

Возвращение объекта по ссылке

В ряде случаев объекты возвращаются по ссылке, например, метод класса NSData initWithContentsOfURL:options: error: в качестве параметра error принимает (NSError **)errorPtr. В этом случае так же работает соглашение по именованию, из которого следует, что явного запроса на владение объектом нет, соответственно, удалять его не требуется.

Когда счетчик ссылок объекта становится равным нулю, объект удаляется. При этом у объекта вызывается метод -(void)dealloc. Если в объекте содержатся какие-то данные, их необходимо удалить в этой функции.

Autorelease pool [ ]

В приложениях Cocoa autorelease пул всегда доступен по умолчанию. Для не-AppKit приложений необходимо создавать и управлять временем жизни autorelease пула самостоятельно.

Autorelease пул реализуется классом NSAutoreleasePool.

Autorelease пул в многопоточной среде

Autorelease пул главного потока периодически пересоздается с целью уменьшения используемой памяти приложением. Во всех остальных потоках заниматься пересозданием autorelease пула необходимо самостоятельно, что крайне актуально для долгоживущих потоков.

Копирование объектов [ ]

Все объекты в Objective-C потенциально поддерживают копирование. Для того, чтобы создать копию объекта, необходимо вызвать метод copy, определённый в классе NSObject. Для создания копии будет вызван метод copyWithZone протокола NSCopying. NSObject не имеет поддержки этого протокола и при необходимости протокол NSCopying должен быть реализован в классах-наследниках.

Копии бывают двух видов: поверхностная копия (shallow copy) и полная копия (deep copy). Разница между этими копиями состоит в том, что при создании поверхностной копии копируются не данные, а ссылка на объект с данными. В случае полной копии копируется объект с данными.

Реализация копирования может различаться в зависимости от того, поддерживает ли класс-родитель протокол NSCopying. Пример кода для ситуации, когда родитель не реализует протокол NSCopying:

Если родитель поддерживает протокол NSCopying, реализация будет несколько иной: вызов allocWithZone заменяется на copyWithZone.

Категории [ ]

Язык Objective-C обладает возможностью добавлять новые методы к уже существующим классам. Аналогичной возможностью обладают языки

Реализация категории выглядит следующим образом:

С помощью категорий можно создавать свойства (property), которые будут доступны только для чтения другим классам и readwrite внутри своего класса:

Кроме всего прочего категории можно использовать для того, чтобы обеспечить реализацию классом какого-либо нового протокола, например:

Это избавляет от необходимости писать класс-адаптер PrintableString для NSString.

Class objects и Objective-C runtime [ ]

Objective-C runtime от Apple содержит большое количество С-функций, служащих для работы с классами (непосредственно во время выполнения программы).

Наиболее интересными являются следующие:

Функция class_getInstanceMethod возвращает указатель на структуру (objc_method), описывающую заданный instance-метод данного класса.

Функция class_getClassMethod возвращает указатель на структуру (objc_method), описывающую заданный метод данного класса.

Функция class_nextMethodList возвращает один из списков методов для заданного класса. Приводимый ниже фрагмент кода позволяет перебрать все методы для данного класса.

Функция class_addMethods позволяет добавлять новые методы к заданному классу.

Функция class_removeMethods позволяет убирать методы из заданного класса.

Функция method_getNumberOfArguments Возвращает количество аргументов для заданного метода.

Функция method_getSizeOfArguments возвращает размер места на стеке, занимаемого всеми аргументами данного метода.

Функция method_getArgumentInfo возвращает информацию об одном из аргументов для заданного метода.

Функция class_getInstanceVariable возвращает информацию об instance-переменной класса в виде указателя на структуру objc_ivar.

Для кодирования информации о типах используется специальное строковое представление, однозначно сопоставляющее каждому типу данных некоторую строку. Явно получить такую строку для произвольного типа можно при помощи конструкции @encode ().

Разное [ ]

Objective-C доступен практически в каждом дистрибутиве Windows используют эмуляторы среды POSIX (бесплатные):

Читайте также: