Objective-C — это высокоуровневый универсальный объектно-ориентированный язык программирования , который добавляет передачу сообщений (сообщений) в стиле Smalltalk к языку программирования C [3] . Первоначально разработанный Брэдом Коксом и Томом Лавом в начале 1980-х годов, он был выбран NeXT для своей операционной системы NeXTSTEP . Из-за прямого происхождения Apple macOS от NeXTSTEP [4] , Objective-C был стандартным языком, используемым, поддерживаемым и продвигаемым Apple для разработки приложений macOS и iOS (через их соответствующие интерфейсы прикладного программирования ( API ), Cocoa и Cocoa Touch ) с 1997 года, когда Apple приобрела NeXT, до появления языка Swift в 2014 году. [3]
Программы Objective-C, разработанные для операционных систем сторонних разработчиков или не зависящие от API Apple, также могут быть скомпилированы для любой платформы, поддерживаемой GNU GNU Compiler Collection (GCC) или LLVM / Clang .
Файлы программ исходного кода Objective-C «сообщения/реализация» обычно имеют .m
расширения имен файлов, в то время как файлы «заголовка/интерфейса» Objective-C имеют .h
расширения, такие же, как и файлы заголовков C. Файлы Objective-C++ обозначаются .mm
расширением файла.
Objective-C был создан главным образом Брэдом Коксом и Томом Лавом в начале 1980-х годов в их компании Productivity Products International (PPI) . [5]
В преддверии создания своей компании они оба познакомились со Smalltalk , работая в Центре технологий программирования корпорации ITT в 1981 году. Самые ранние работы над Objective-C относятся примерно к тому времени. [6] Кокс был заинтригован проблемами истинной возможности повторного использования в проектировании и программировании программного обеспечения. Он понял, что такой язык, как Smalltalk, будет бесценен при создании сред разработки для разработчиков систем в ITT. Однако он и Том Лав также осознали, что обратная совместимость с C была критически важна в среде инженеров телекоммуникаций ITT. [7]
Кокс начал писать препроцессор для C, чтобы добавить некоторые возможности Smalltalk . Вскоре у него появилась рабочая реализация объектно-ориентированного расширения языка C , которое он назвал Object-Oriented Pre-Compiler (OOPC). [8] Лав был нанят Schlumberger Research в 1982 году и имел возможность приобрести первую коммерческую копию Smalltalk-80, что еще больше повлияло на развитие их детища. Чтобы продемонстрировать, что можно добиться реального прогресса, Кокс показал, что для создания взаимозаменяемых программных компонентов на самом деле требуется всего несколько практических изменений в существующих инструментах. В частности, им нужно было поддерживать объекты гибким образом, поставляться с пригодным для использования набором библиотек и позволять коду (и любым ресурсам, необходимым коду) быть объединенными в один кроссплатформенный формат.
В конечном итоге Лав и Кокс создали PPI для коммерциализации своего продукта, который объединил компилятор Objective-C с библиотеками классов. В 1986 году Кокс опубликовал основное описание Objective-C в его первоначальной форме в книге Object-Oriented Programming, An Evolutionary Approach . Хотя он тщательно объяснил, что проблема повторного использования заключается не только в том, что предоставляет Objective-C, язык часто сравнивали по функциям с другими языками.
В 1988 году NeXT лицензировала Objective-C у StepStone (новое название PPI, владельца торговой марки Objective-C) и расширила компилятор GCC для поддержки Objective-C. NeXT разработала библиотеки Application Kit (AppKit) и Foundation Kit , на которых были основаны пользовательский интерфейс NeXTSTEP и Interface Builder. Хотя рабочие станции NeXT не оказали большого влияния на рынок, эти инструменты получили широкое признание в отрасли. NeXT прекратила производство оборудования и сосредоточилась на программных инструментах, продавая NeXTSTEP (и OPENSTEP) как платформу для пользовательского программирования.
Чтобы обойти условия GPL , NeXT изначально намеревалась поставлять фронтенд Objective-C отдельно, позволяя пользователю связывать его с GCC для создания исполняемого файла компилятора. Хотя изначально Ричард М. Столлман принял этот план, он был отвергнут после того, как Столлман проконсультировался с юристами GNU, и NeXT согласилась сделать Objective-C частью GCC. [9]
Работу по расширению GNU Compiler Collection (GCC) возглавил Стив Нарофф, который присоединился к NeXT из StepStone. Изменения компилятора были доступны в соответствии с условиями GNU General Public License (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 унаследовал свой объектный синтаксис от Smalltalk . Весь синтаксис для необъектно-ориентированных операций (включая примитивные переменные, предварительную обработку, выражения, объявления функций и вызовы функций) идентичен синтаксису C, в то время как синтаксис для объектно-ориентированных функций представляет собой реализацию обмена сообщениями в стиле Smalltalk.
Модель Objective-C объектно-ориентированного программирования основана на передаче сообщений экземплярам объектов. В Objective-C не вызывается метод ; отправляется сообщение . Это не похоже на модель программирования в стиле Simula , используемую в C++ . Разница между этими двумя концепциями заключается в том, как выполняется код, на который ссылается имя метода или сообщения. В языке в стиле Simula имя метода в большинстве случаев привязывается компилятором к разделу кода в целевом классе. В Smalltalk и Objective-C цель сообщения разрешается во время выполнения, при этом принимающий объект сам интерпретирует сообщение. Метод идентифицируется селектором или SEL — уникальным идентификатором для каждого имени сообщения, часто просто NUL
строкой с символом в конце, представляющей его имя, — и разрешается в указатель метода C, реализующий его: IMP. [18] Следствием этого является то, что система передачи сообщений не имеет проверки типов. Объект, которому направлено сообщение — получатель — не гарантирует, что ответит на сообщение, и если он этого не сделает, он вызовет исключение. [19]
Для отправки сообщения method
объекту, на который указывает указатель, obj
потребуется следующий код на C++ :
объект -> метод ( аргумент );
В Objective-C это записывается следующим образом:
[ метод объекта : аргумент ];
Вызов "method" транслируется компилятором в objc_msgSend(id self, SEL op, ...)
семейство функций времени выполнения. Различные реализации обрабатывают современные дополнения, такие как super
. [20] В семействах GNU эта функция называется objc_msg_sendv
, но она была устарела в пользу современной системы поиска в objc_msg_lookup
. [21]
Оба стиля программирования имеют множество сильных и слабых сторон. Объектно-ориентированное программирование в стиле Simula ( C++ ) допускает множественное наследование и более быстрое выполнение с использованием связывания во время компиляции , когда это возможно, но оно не поддерживает динамическое связывание по умолчанию. Оно также заставляет все методы иметь соответствующую реализацию, если они не являются абстрактными . Программирование в стиле Smalltalk, используемое в Objective-C, позволяет сообщениям оставаться нереализованными, с методом, разрешенным к его реализации во время выполнения. Например, сообщение может быть отправлено коллекции объектов, на которую, как ожидается, ответят только некоторые из них, без опасения возникновения ошибок во время выполнения. Передача сообщений также не требует, чтобы объект был определен во время компиляции . Реализация по-прежнему требуется для вызова метода в производном объекте. (См. раздел динамической типизации ниже для получения дополнительных преимуществ динамического (позднего) связывания.)
Objective-C требует, чтобы интерфейс и реализация класса были в отдельно объявленных блоках кода. По соглашению разработчики помещают интерфейс в файл заголовка , а реализацию — в файл кода. Файлы заголовков, обычно имеющие суффикс .h, похожи на файлы заголовков C, в то время как файлы реализации (методов), обычно имеющие суффикс .m, могут быть очень похожи на файлы кода C.
Это аналогично объявлениям классов, используемым в других объектно-ориентированных языках, таких как C++ или Python.
Интерфейс класса обычно определяется в заголовочном файле. Распространенное соглашение — называть заголовочный файл по имени класса, например, Ball.h
будет содержать интерфейс для класса Ball
.
Декларация интерфейса имеет вид:
@interface classname : superclassname { // переменные экземпляра } + classMethod1 ; + ( return_type ) classMethod2 ; + ( return_type ) classMethod3: ( param1_type ) param1_varName ; - ( тип_возвращения ) экземплярМетод1С1Параметр: ( тип1_параметра ) имя_переменной1_параметра ; - ( return_type ) instanceMethod2With2Parameters: ( param1_type ) param1_varName param2_callName :( param2_type ) param2_varName ; @конец
В приведенном выше примере знаки плюс обозначают методы класса или методы, которые могут быть вызваны для самого класса (не для экземпляра), а знаки минус обозначают методы экземпляра , которые могут быть вызваны только для конкретного экземпляра класса. Методы класса также не имеют доступа к переменным экземпляра .
Приведенный выше код примерно эквивалентен следующему интерфейсу C++ :
класс имя_класса : имя_суперкласса public { protected : // переменные экземпляра public : // Функции класса (статические) static void * classMethod1 (); static return_type classMethod2 (); static return_type classMethod3 ( param1_type param1_varName ); // Функции экземпляра (члена) return_type instanceMethod1With1Parameter ( param1_type param1_varName ); return_type instanceMethod2With2Parameters ( param1_type param1_varName , param2_type param2_varName = default ); };
Обратите внимание, что instanceMethod2With2Parameters:param2_callName:
здесь демонстрируется чередование сегментов селектора с выражениями аргументов, для которых нет прямого эквивалента в C/C++.
Типы возвращаемых данных могут быть любым стандартным типом C , указателем на общий объект Objective-C, указателем на определенный тип объекта, такой как NSArray *, NSImage * или NSString *, или указателем на класс, к которому принадлежит метод (instancetype). Тип возвращаемых данных по умолчанию — общий тип Objective-C id
.
Аргументы метода начинаются с имени, маркирующего аргумент, который является частью имени метода, за которым следует двоеточие, за которым следует ожидаемый тип аргумента в скобках и имя аргумента. Метку можно опустить.
- ( void ) setRangeStart: ( int ) начало конец: ( int ) конец ; - ( void ) importDocumentWithName: ( NSString * ) имя withSpecifiedPreferences :( Preferences * ) prefs beforePage :( int ) insertPage ;
Производной определения интерфейса является категория , которая позволяет добавлять методы к существующим классам. [22]
Интерфейс объявляет только интерфейс класса, а не сами методы: фактический код записывается в файле реализации. Файлы реализации (методов) обычно имеют расширение файла .m
, которое изначально означало «сообщения». [23]
@implementation имя_класса + ( return_type ) classMethod { // реализация } - ( return_type ) instanceMethod { // реализация } @end
Методы пишутся с использованием деклараций их интерфейсов. Сравнение Objective-C и C:
- ( int ) метод: ( int ) i { return [ self square_root : i ]; }
функция int ( int i ) { return square_root ( i ); }
Синтаксис допускает псевдоименование аргументов .
- ( void ) changeColorToRed: ( float ) красный зеленый: ( float ) зеленый синий: ( float ) синий { //... Реализация ... } // Вызывается так: [ myColor changeColorToRed : 5.0 green : 2.0 blue : 6.0 ];
Внутренние представления метода различаются в разных реализациях Objective-C. Если myColor принадлежит классу Color
, метод экземпляра -changeColorToRed:green:blue:
может быть внутренне помечен _i_Color_changeColorToRed_green_blue
. is i
должен ссылаться на метод экземпляра с добавлением имен класса и метода, а двоеточия заменяются на подчеркивания. Поскольку порядок параметров является частью имени метода, его нельзя изменить в соответствии со стилем кодирования или выражением, как в случае с истинно именованными параметрами.
Однако внутренние имена функции редко используются напрямую. Обычно сообщения преобразуются в вызовы функций, определенные в библиотеке времени выполнения Objective-C. Во время компоновки не обязательно известно, какой метод будет вызван, поскольку класс получателя (объекта, которому отправляется сообщение) не обязательно должен быть известен до времени выполнения.
После того, как класс Objective-C написан, его можно инстанцировать. Это делается путем первого выделения неинициализированного экземпляра класса (объекта), а затем его инициализации. Объект не является полностью функциональным, пока не будут выполнены оба шага. Эти шаги должны быть выполнены одной строкой кода, чтобы никогда не было выделенного объекта, который не прошел инициализацию (и потому что неразумно сохранять промежуточный результат, так как -init
он может вернуть другой объект, нежели тот, для которого он был вызван).
Создание экземпляра с инициализатором по умолчанию без параметров:
MyObject * foo = [[ MyObject alloc ] init ];
Создание экземпляра с помощью пользовательского инициализатора:
MyObject * foo = [[ MyObject alloc ] initWithString : myString ];
В случае, когда не выполняется пользовательская инициализация, вместо сообщений alloc-init часто можно использовать метод «new»:
МойОбъект * foo = [ МойОбъект новый ];
Также некоторые классы реализуют инициализаторы методов класса. Как и +new
, они объединяют +alloc
и -init
, но в отличие от +new
, они возвращают автоматически выпущенный экземпляр. Некоторые инициализаторы методов класса принимают параметры:
MyObject * foo = [ MyObject object ]; MyObject * bar = [ MyObject objectWithString : @"Wikipedia :)" ];
Сообщение alloc выделяет достаточно памяти для хранения всех переменных экземпляра объекта, устанавливает все переменные экземпляра в нулевые значения и превращает память в экземпляр класса; ни на одном этапе инициализации память не является экземпляром суперкласса.
Сообщение init выполняет настройку экземпляра при создании. Метод init часто записывается следующим образом:
- ( id ) инициализация { self = [ супер инициализация ]; если ( я ) { // здесь выполняем инициализацию объекта } вернуть себя ; }
В приведенном выше примере обратите внимание на id
возвращаемый тип. Этот тип обозначает указатель на любой объект в Objective-C (см. раздел Динамическая типизация).
Шаблон инициализатора используется для обеспечения правильной инициализации объекта его суперклассом до того, как метод init выполнит его инициализацию. Он выполняет следующие действия:
init
сообщение и присваивает результат self
(указатель на текущий объект).Недопустимый указатель на объект имеет значение nil
; условные операторы типа if
обрабатывают nil как нулевой указатель, поэтому код инициализации не будет выполнен, если [super init]
возвращен nil. Если при инициализации возникла ошибка, метод init должен выполнить всю необходимую очистку, включая отправку release
сообщения self, и вернуться, nil
чтобы указать, что инициализация не удалась. Любая проверка на наличие таких ошибок должна выполняться только после вызова инициализации суперкласса, чтобы гарантировать, что уничтожение объекта будет выполнено правильно.
Если класс имеет более одного метода инициализации, только один из них ( назначенный инициализатор ) должен следовать этому шаблону; остальные должны вызывать назначенный инициализатор вместо инициализатора суперкласса.
В других языках программирования они называются интерфейсами .
Objective-C был расширен в NeXT, чтобы ввести концепцию множественного наследования спецификации, но не реализации, посредством введения протоколов . Это шаблон, достижимый либо как абстрактный множественно наследуемый базовый класс в C++ , либо как интерфейс (как в Java и C# ). Objective-C использует специальные протоколы, называемые неформальными протоколами , и протоколы, навязываемые компилятором, называемые формальными протоколами .
Неформальный протокол — это список методов, которые класс может выбрать для реализации. Он указан в документации, поскольку не представлен в языке. Неформальные протоколы реализованы как категория (см. ниже) на NSObject и часто включают необязательные методы, которые, если реализованы, могут изменить поведение класса. Например, класс текстового поля может иметь делегата , который реализует неформальный протокол с необязательным методом для выполнения автодополнения введенного пользователем текста. Текстовое поле обнаруживает, реализует ли делегат этот метод (через рефлексивное программирование (рефлексию)) и, если да, вызывает метод делегата для поддержки функции автодополнения.
Формальный протокол похож на интерфейс в Java, C# и Ada 2005. Это список методов, которые любой класс может объявить для реализации. Версии Objective-C до 2.0 требовали, чтобы класс реализовал все методы в протоколе, который он объявляет как принимающий; компилятор выдаст ошибку, если класс не реализует все методы из своих объявленных протоколов. В Objective-C 2.0 добавлена поддержка маркировки определенных методов в протоколе как необязательных, и компилятор не будет принудительно выполнять реализацию необязательных методов.
Класс должен быть объявлен для реализации этого протокола, чтобы можно было сказать, что он соответствует ему. Это обнаруживается во время выполнения. Формальные протоколы не могут предоставлять никаких реализаций; они просто гарантируют вызывающим, что классы, соответствующие протоколу, предоставят реализации. В библиотеке NeXT/Apple протоколы часто используются системой распределенных объектов для представления возможностей объекта, выполняемого на удаленной системе.
Синтаксис
@protocol NSLocking - ( void ) заблокировать ; - ( void ) разблокировать ; @end
обозначает, что есть абстрактная идея блокировки. Указывая в определении класса, что протокол реализован,
@interface NSLock : NSObject < NSLocking > // ... @end
экземпляры NSLock утверждают, что они предоставят реализацию для двух методов экземпляра.
Objective-C, как и Smalltalk, может использовать динамическую типизацию : объекту можно отправить сообщение, которое не указано в его интерфейсе. Это может обеспечить большую гибкость, поскольку позволяет объекту «захватить» сообщение и отправить его другому объекту, который может соответствующим образом отреагировать на сообщение или аналогичным образом отправить сообщение другому объекту. Такое поведение известно как пересылка сообщений или делегирование (см. ниже). В качестве альтернативы можно использовать обработчик ошибок в случае, если сообщение не может быть переслано. Если объект не пересылает сообщение, не отвечает на него или не обрабатывает ошибку, то система сгенерирует исключение времени выполнения. [24] Если сообщения отправляются в nil (указатель на нулевой объект), они будут молча игнорироваться или вызывать общее исключение, в зависимости от параметров компилятора.
Статическая типизированная информация также может быть необязательно добавлена к переменным. Эта информация затем проверяется во время компиляции. В следующих четырех операторах предоставляется все более конкретная информация о типе. Операторы эквивалентны во время выполнения, но дополнительная информация позволяет компилятору предупредить программиста, если переданный аргумент не соответствует указанному типу.
- ( void ) setMyValue: ( id ) foo ;
В приведенном выше утверждении foo может относиться к любому классу.
- ( void ) setMyValue: ( id < NSCopying > ) foo ;
В приведенном выше утверждении foo может быть экземпляром любого класса, соответствующего протоколу NSCopying
.
- ( void ) setMyValue: ( NSNumber * ) foo ;
В приведенном выше утверждении foo должен быть экземпляром класса NSNumber .
- ( void ) setMyValue: ( NSNumber < NSCopying > * ) foo ;
В приведенном выше утверждении foo должен быть экземпляром класса NSNumber и должен соответствовать протоколу NSCopying
.
В Objective-C все объекты представлены как указатели, а статическая инициализация не допускается. Простейшим объектом является тип, на который указывает id ( objc_obj * ), который имеет только указатель isa, описывающий его класс. Другие типы из C, такие как значения и структуры, остаются неизменными, поскольку они не являются частью объектной системы. Это решение отличается от объектной модели C++, где структуры и классы объединены.
Objective-C позволяет отправлять сообщение объекту, который может не ответить. Вместо того, чтобы отвечать или просто отбрасывать сообщение, объект может переслать сообщение объекту, который может ответить. Пересылка может использоваться для упрощения реализации определенных шаблонов проектирования , таких как шаблон наблюдателя или шаблон прокси .
Среда выполнения Objective-C определяет пару методов вObject
- ( retval_t ) forward: ( SEL ) sel args: ( arglist_t ) args ; // с GCC - ( id ) forward: ( SEL ) sel args: ( marg_list ) args ; // с системами NeXT/Apple
- ( retval_t ) performv: ( SEL ) sel args: ( arglist_t ) args ; // с GCC - ( id ) performv: ( SEL ) sel args: ( marg_list ) args ; // с системами NeXT/Apple
Объекту, желающему реализовать пересылку, нужно только переопределить метод пересылки новым методом, чтобы определить поведение пересылки. Метод действия performv::
не нужно переопределять, так как этот метод просто выполняет действие на основе селектора и аргументов. Обратите внимание на SEL
тип, который является типом сообщений в Objective-C.
Примечание: в OpenStep, Cocoa и GNUstep, часто используемых фреймворках Objective-C, класс не используется Object
. Метод класса используется для пересылки.- (void)forwardInvocation:(NSInvocation *)anInvocation
NSObject
Вот пример программы, демонстрирующей основы пересылки.
#импорт <objc/Object.h>@interface Forwarder : Object { id receiveer ; // Объект, которому мы хотим переслать сообщение. } // Методы доступа. - ( id ) получатель ; - ( id ) setRecipient: ( id ) _recipient ; @end
#импорт "Forwarder.h"@implementation Forwarder - ( retval_t ) forward: ( SEL ) sel args: ( arglist_t ) args { /* * Проверяет, действительно ли получатель отвечает на сообщение. * Это может быть желательно или нет, например, если получатель * в свою очередь не отвечает на сообщение, он может выполнить пересылку * сам. */ if ([ receiveer responsesToSelector : sel ]) { return [ receiveer performv : sel args : args ]; } else { return [ self error : "Recipient does not respond" ]; } } - ( id ) setRecipient: ( id ) _recipient { [ автоматическое освобождение получателя ]; получатель = [ сохранение _recipient ]; возврат себя ; } - ( id ) получатель { return получатель ; } @end
#импорт <objc/Object.h>// Простой объект получателя. @interface Получатель : Объект - ( id ) hello ; @end
#импорт "Получатель.h"@implementation Получатель- ( id ) hello { printf ( "Получатель передает привет! \n " ); вернуть себя ; } @конец
#импорт "Пересылка.h" #импорт "Получатель.h"int main ( void ) { Пересылающий * пересылающий = [ Новый пересылающий ]; Получатель * получатель = [ Новый получатель ]; [ forwarder setRecipient : receive ]; // Устанавливаем получателя. /* * Наблюдаем, что пересылающий не отвечает на сообщение hello! Оно будет * переслано. Все нераспознанные методы будут пересланы * получателю * (если получатель ответит на них, как написано в Forwarder) */ [ forwarder hello ]; [ выпуск получателя ]; [ выпуск пересылки ]; вернуть 0 ; }
При компиляции с использованием gcc компилятор сообщает:
$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc main.m: В функции `main': main.m:12: предупреждение: `Forwarder' не отвечает на `hello' $
Компилятор сообщает о точке, указанной ранее, которая Forwarder
не отвечает на сообщения hello. В этом случае можно смело игнорировать предупреждение, поскольку была реализована пересылка. Запуск программы дает следующий вывод:
$ ./a.out Получатель передает привет!
Во время проектирования Objective-C одной из главных проблем была поддерживаемость больших кодовых баз. Опыт из мира структурного программирования показал, что один из основных способов улучшения кода — разбить его на более мелкие части. Objective-C заимствовал и расширил концепцию категорий из реализаций Smalltalk, чтобы помочь с этим процессом. [25]
Более того, методы в категории добавляются в класс во время выполнения . Таким образом, категории позволяют программисту добавлять методы в существующий класс — открытый класс — без необходимости перекомпилировать этот класс или даже иметь доступ к его исходному коду. Например, если система не содержит проверку орфографии в своей реализации String, ее можно добавить без изменения исходного кода String.
Методы внутри категорий становятся неотличимы от методов в классе при запуске программы. Категория имеет полный доступ ко всем переменным экземпляра внутри класса, включая частные переменные.
Если категория объявляет метод с той же сигнатурой метода , что и существующий метод в классе, метод категории принимается. Таким образом, категории могут не только добавлять методы в класс, но и заменять существующие методы. Эту функцию можно использовать для исправления ошибок в других классах путем переписывания их методов или для глобального изменения поведения класса в программе. Если две категории имеют методы с одинаковым именем, но разными сигнатурами методов, не определено, какой метод категории принимается.
Другие языки пытались добавить эту функцию различными способами. TOM продвинул систему Objective-C на шаг дальше и также позволил добавлять переменные. Другие языки использовали вместо этого решения на основе прототипов , наиболее заметным из которых является Self .
Языки C# и Visual Basic (.NET) реализуют внешне схожие функции в форме методов расширения , но у них нет доступа к закрытым переменным класса. [26] Ruby и несколько других динамических языков программирования называют эту технику « обезьяньей патчей ».
Logtalk реализует концепцию категорий (как сущностей первого класса), которая включает в себя функцию категорий Objective-C (категории Logtalk также могут использоваться как мелкозернистые единицы композиции при определении, например, новых классов или прототипов; в частности, категория Logtalk может быть виртуально импортирована любым количеством классов и прототипов).
В этом примере создается Integer
класс, сначала определяющий базовый класс с реализованными только методами доступа , и добавляющий две категории Arithmetic
и Display
, которые расширяют базовый класс. Хотя категории могут получать доступ к закрытым членам данных базового класса, часто хорошей практикой является доступ к этим закрытым членам данных через методы доступа, что помогает сохранять категории более независимыми от базового класса. Реализация таких методов доступа является одним из типичных применений категорий. Другое — использование категорий для добавления методов в базовый класс. Однако не считается хорошей практикой использование категорий для переопределения подклассов, также известного как monkey patching . Неформальные протоколы реализуются как категория в базовом NSObject
классе. По соглашению файлы, содержащие категории, которые расширяют базовые классы, будут иметь имя BaseClass+ExtensionClass.h .
#импорт <objc/Object.h>@interface Целое число : Объект { int целое число ; } - ( int ) целое число ; - ( id ) целое число: ( int ) _целое число ; @end
#импорт "Integer.h"@implementation Целое число - ( int ) целое число { return целое число ; } - ( id ) целое число: ( int ) _целое число { целое число = _целое число ; вернуть себя ; } @end
#импорт "Integer.h"@interface Целое число (Арифметика) - ( id ) сложение: ( Целое число * ) слагаемое ; - ( id ) вычитаемое: ( Целое число * ) вычитаемое ; @end
#импорт "Целое+Арифметика.h"@implementation Целое число (Арифметика) - ( id ) add: ( Целое число * ) addend { return [ self integer : [ self integer ] + [ addend integer ]]; } - ( id ) sub: ( Integer * ) subtrahend { return [ self integer : [ self integer ] - [ subtrahend integer ]]; } @end
#импорт "Integer.h"@interface Integer (Display) - ( id ) showstars ; - ( id ) showint ; @end
# импорт "Integer+Display.h"@implementation Integer (Display) - ( id ) showstars { int i , x = [ self integer ]; for ( i = 0 ; i < x ; i ++ ) { printf ( "*" ); } printf ( " \n " ); вернуть себя ; } - ( id ) showint { printf ( "%d \n " , [ self integer ]); вернуть себя ; } @end
#импорт "Integer.h"#импорт "Целое+Арифметика.h"#импорт "Integer+Display.h"int main ( void ) { Целое число * num1 = [ Целое число новое ], * num2 = [ Целое число новое ]; int x ; printf ( "Введите целое число: " ); scanf ( "%d" , & x ); [ num1 целое число : x ]; [ num1 шоу-звезды ]; printf ( "Введите целое число: " ); scanf ( "%d" , & x ); [ num2 целое число : x ]; [ num2 шоу-звезды ]; [ num1 добавить : num2 ]; [ num1 showint ]; вернуть 0 ; }
Компиляция выполняется, например, следующим образом:
$ gcc -x objective-c main.m Целое число.m Целое число+Арифметика.m Целое число+Отображение.m -lobjc
Можно поэкспериментировать, опустив #import "Integer+Arithmetic.h"
(строку 2) и (строку 21) и исключив их из компиляции. Программа все равно будет работать. Это означает, что можно смешивать и сопоставлять добавленные категории, если это необходимо; если категории не нужно иметь какую-то способность, ее можно просто не компилировать.[num1 add:num2]
Integer+Arithmetic.m
Objective-C позволяет классу полностью заменить другой класс в программе. Говорят, что заменяющий класс «выдает себя» за целевой класс.
Позиция класса была объявлена устаревшей в Mac OS X v10.5 и недоступна в 64-битной среде выполнения. Подобную функцию можно получить, используя метод swizzling в категориях, который меняет реализацию одного метода на реализацию другого, имеющего ту же сигнатуру.
Для версий, которые все еще поддерживают позирование, все сообщения, отправленные целевому классу, вместо этого принимаются классом позирования. Существует несколько ограничений:
Позиция, подобно категориям, позволяет глобальное расширение существующих классов. Позиция допускает две функции, отсутствующие в категориях:
Например,
@interface CustomNSApplication : NSApplication @end@implementation CustomNSApplication - ( void ) setMainMenu: ( NSMenu * ) menu { // сделать что-то с меню } @end class_poseAs ([ Класс CustomNSApplication ], [ Класс NSApplication ]);
Это перехватывает каждый вызов setMainMenu в NSApplication.
В языке C #include
директива pre-compile всегда вызывает вставку содержимого файла в исходный код в этой точке. В Objective-C есть директива #import
, эквивалентная той, что каждый файл включается только один раз на единицу компиляции, что устраняет необходимость в include guards .
// ФАЙЛ: hello.m #import <Foundation/Foundation.h> int main ( int argc , const char * argv []) { /* моя первая программа на Objective-C */ NSLog ( @"Hello, World! \n " ); return 0 ; }
$ # Командная строка компиляции для компилятора gcc и MinGW: $ gcc \ $ ( gnustep-config --objc-flags ) \ -o hello \ hello.m \ -L /GNUstep/System/Library/Libraries \ -lobjc \ -lgnustep-base $ ./привет
Возможности Objective-C часто позволяют находить гибкие и простые решения проблем программирования.
Objective-C++ — это вариант языка, принятый front-end для GNU Compiler Collection и Clang , который может компилировать исходные файлы, использующие комбинацию синтаксиса C++ и Objective-C. Objective-C++ добавляет к C++ расширения, которые Objective-C добавляет к C. Поскольку ничего не делается для унификации семантики, лежащей в основе различных языковых функций, применяются определенные ограничения:
Classname *
) могут использоваться как параметры шаблона C++.На Всемирной конференции разработчиков 2006 года Apple объявила о выпуске «Objective-C 2.0», пересмотренного варианта языка Objective-C, включающего «современную сборку мусора, улучшения синтаксиса, [29] улучшения производительности среды выполнения, [30] и поддержку 64-битной среды». Mac OS X v10.5 , выпущенная в октябре 2007 года, включала компилятор Objective-C 2.0. GCC 4.6 поддерживает множество новых функций Objective-C, таких как объявленные и синтезированные свойства, точечный синтаксис, быстрое перечисление, необязательные методы протокола, атрибуты метода/протокола/класса, расширения классов и новый API среды выполнения GNU Objective-C. [31]
Название Objective-C 2.0 представляет собой разрыв в системе управления версиями языка, поскольку последняя версия Objective-C для NeXT была «objc4». [32] Это название проекта было сохранено в последнем выпуске исходного кода среды выполнения Objective-C в Mac OS X Leopard (10.5). [33]
Objective-C 2.0 предоставлял опциональный консервативный сборщик мусора поколений . При запуске в режиме обратной совместимости среда выполнения превращала операции подсчета ссылок , такие как «retain» и «release», в пустые операции . Все объекты подвергались сборке мусора, когда сборка мусора была включена. Обычные указатели C могли быть квалифицированы с помощью «__strong», чтобы также вызывать базовые перехваты компилятора барьера записи и, таким образом, участвовать в сборке мусора. [34] Также была предоставлена слабая подсистема обнуления, так что указатели, помеченные как «__weak», устанавливались в ноль, когда объект (или, проще говоря, память GC) собирается. Сборщик мусора не существует в реализации Objective-C 2.0 для iOS. [35] Сборка мусора в Objective-C выполняется в фоновом потоке с низким приоритетом и может останавливаться при пользовательских событиях с целью сохранения отзывчивости пользовательского опыта. [36]
Сборка мусора была отменена в Mac OS X v10.8 в пользу автоматического подсчета ссылок (ARC). [37] Objective-C на iOS 7, работающем на ARM64, использует 19 бит из 64-битного слова для хранения счетчика ссылок в виде помеченных указателей . [38] [39]
Objective-C 2.0 вводит новый синтаксис для объявления переменных экземпляра как свойств с необязательными атрибутами для настройки генерации методов доступа. Свойства, в некотором смысле, являются публичными переменными экземпляра; то есть объявление переменной экземпляра как свойства предоставляет внешним классам доступ (возможно, ограниченный, например, только для чтения) к этому свойству. Свойство может быть объявлено как "только для чтения" и может быть снабжено семантикой хранения, такой как assign
, copy
или retain
. По умолчанию свойства считаются atomic
, что приводит к блокировке, предотвращающей одновременный доступ к ним нескольких потоков. Свойство может быть объявлено как nonatomic
, что снимает эту блокировку.
@interface Person : NSObject { @public NSString * имя ; @private int возраст ; } @property ( копия ) NSString * имя ; @property ( только чтение ) int возраст ; - ( id ) initWithAge: ( int ) age ; @end
Свойства реализуются с помощью @synthesize
ключевого слова, которое генерирует методы getter (и setter, если они не только для чтения) в соответствии с объявлением свойства. В качестве альтернативы методы getter и setter должны быть реализованы явно, или @dynamic
ключевое слово может использоваться для указания того, что методы доступа будут предоставлены другими способами. При компиляции с использованием clang 3.1 или выше все свойства, которые явно не объявлены с помощью @dynamic
, помечены readonly
или имеют полностью реализованные пользователем getter и setter, будут автоматически неявно @synthesize
'd.
@implementation Person @synthesize имя ; - ( id ) initWithAge: ( int ) initAge { self = [ super init ]; if ( self ) { // ПРИМЕЧАНИЕ: прямое назначение переменной экземпляра, а не установка свойства age = initAge ; } return self ; } - ( целое ) возраст { возврат возраста ; } @end
Доступ к свойствам можно получить с помощью традиционного синтаксиса передачи сообщений, точечной нотации или, в кодировании «ключ-значение», по имени с помощью методов «valueForKey:»/«setValue:forKey:».
Person * aPerson = [[ Person alloc ] initWithAge : 53 ]; aPerson . name = @"Steve" ; // ПРИМЕЧАНИЕ: точечная нотация, использует синтезированный сеттер, // эквивалентно [aPerson setName: @"Steve"]; NSLog ( @"Доступ по сообщению (%@), точечная нотация (%@), имя свойства (% @) и " "прямой доступ к переменной экземпляра (% @) " , [ aPerson name ], aPerson . name , [ aPerson valueForKey : @"name" ], aPerson -> name );
Чтобы использовать точечную нотацию для вызова методов доступа к свойствам внутри метода экземпляра, self
следует использовать ключевое слово:
- ( void ) introduceMyselfWithProperties: ( BOOL ) useGetter { NSLog ( @"Привет, меня зовут %@." , ( useGetter ? self . name : name )); // ПРИМЕЧАНИЕ: доступ к getter и ivar }
Свойства класса или протокола могут быть подвергнуты динамическому интроспекции .
int i ; int propertyCount = 0 ; objc_property_t * propertyList = class_copyPropertyList ([ aPerson class ], & propertyCount ); for ( i = 0 ; i < propertyCount ; i ++ ) { objc_property_t * thisProperty = propertyList + i ; const char * propertyName = property_getName ( * thisProperty ); NSLog ( @"У человека есть свойство: '%s'" , propertyName ); }
Objective-C 2.0 предоставляет нехрупкие переменные экземпляра, где они поддерживаются средой выполнения (например, при сборке кода для 64-битной macOS и всех iOS). В современной среде выполнения к доступу к переменным экземпляра добавляется дополнительный уровень косвенности, что позволяет динамическому компоновщику корректировать макет экземпляра во время выполнения. Эта функция позволяет внести два важных улучшения в код Objective-C:
Вместо использования объекта NSEnumerator или индексов для итерации по коллекции Objective-C 2.0 предлагает быстрый синтаксис перечисления. В Objective-C 2.0 следующие циклы функционально эквивалентны, но имеют разные характеристики производительности.
// Использование NSEnumerator NSEnumerator * enumerator = [ thePeople objectEnumerator ]; Person * p ; while (( p = [ enumerator nextObject ]) != nil ) { NSLog ( @"%@ is %i years old." , [ p name ], [ p age ]); }
// Использование индексов for ( int i = 0 ; i < [ thePeople count ]; i ++ ) { Person * p = [ thePeople objectAtIndex : i ]; NSLog ( @"%@ is %i years old." , [ p name ], [ p age ]); }
// Используем быстрое перечисление for ( Person * p in thePeople ) { NSLog ( @"%@ is %i years old." , [ p name ], [ p age ]); }
Быстрое перечисление генерирует более эффективный код, чем стандартное перечисление, поскольку вызовы методов для перечисления объектов заменяются арифметикой указателей с использованием протокола NSFastEnumeration. [40]
Расширение класса имеет тот же синтаксис, что и объявление категории без имени категории, а методы и свойства, объявленные в нем, добавляются непосредственно в основной класс. В основном оно используется как альтернатива категории для добавления методов в класс без их объявления в общедоступных заголовках, с тем преимуществом, что для расширений класса компилятор проверяет, что все объявленные в частном порядке методы фактически реализованы. [41]
Все приложения Objective-C, разработанные для macOS, которые используют вышеуказанные улучшения для Objective-C 2.0, несовместимы со всеми операционными системами до 10.5 (Leopard). Поскольку быстрое перечисление не генерирует точно такие же двоичные файлы, как стандартное перечисление, его использование приведет к сбою приложения на Mac OS X версии 10.4 или более ранней.
Блоки — это нестандартное расширение для Objective-C (а также C и C++ ), которое использует специальный синтаксис для создания замыканий . Блоки поддерживаются только в Mac OS X 10.6 "Snow Leopard" или более поздней версии, iOS 4 или более поздней версии и GNUstep с libobjc2 1.7 и компиляцией с помощью clang 3.1 или более поздней версии. [42]
#include <stdio.h> #include <Block.h> typedef int ( ^ IntBlock )(); IntBlock MakeCounter ( int start , int increment ) { __block int i = start ; return Block_copy ( ^ { int ret = i ; i += increment ; return ret ; }); }int main ( void ) { IntBlock mycounter = MakeCounter ( 5 , 2 ); printf ( "Первый вызов: %d \n " , mycounter ()); printf ( "Второй вызов: %d \n " , mycounter ()); printf ( "Третий вызов: %d \n " , mycounter ()); /* поскольку он был скопирован, его также необходимо освободить */ Block_release ( mycounter ); return 0 ; } /* Вывод: Первый вызов: 5 Второй вызов: 7 Третий вызов: 9 */
Apple со временем добавила несколько дополнительных функций в Objective 2.0. Дополнения применяются только к «компилятору Apple LLVM », т. е. к интерфейсу clang языка. Сбивает с толку то, что версия, используемая Apple, отличается от версии LLVM upstream; см. Xcode § Toolchain версии для перевода в номера версий LLVM с открытым исходным кодом. [43]
Автоматический подсчет ссылок (ARC) — это функция времени компиляции, которая устраняет необходимость для программистов вручную управлять счетчиками сохранения с помощью retain
и release
. [44] В отличие от сборки мусора , которая происходит во время выполнения, ARC устраняет накладные расходы отдельного процесса, управляющего счетчиками сохранения. ARC и ручное управление памятью не являются взаимоисключающими; программисты могут продолжать использовать код, не являющийся ARC, в проектах с поддержкой ARC, отключив ARC для отдельных файлов кода. Xcode также может попытаться автоматически обновить проект до ARC.
ARC был представлен в LLVM 3.0. Это переводится как Xcode 4.2 (2011) или Apple LLVM compiler 3.0. [45]
Среды выполнения NeXT и Apple Obj-C уже давно включают в себя краткий способ создания новых строк, используя литеральный синтаксис @"a new string"
или переход к константам CoreFoundation kCFBooleanTrue
и kCFBooleanFalse
for NSNumber
с булевыми значениями. Использование этого формата избавляет программиста от необходимости использовать более длинные initWithString
или похожие методы при выполнении определенных операций.
При использовании компилятора Apple LLVM 4.0 (Xcode 4.4) или более поздней версии массивы, словари и числа ( NSArray
, NSDictionary
, NSNumber
классы) также могут быть созданы с использованием литерального синтаксиса вместо методов. [46] (Компилятор Apple LLVM 4.0 транслируется в LLVM с открытым исходным кодом и Clang 3.1.) [47]
Пример без литералов:
NSArray * myArray = [ NSArray arrayWithObjects : object1 , object2 , object3 , nil ]; NSDictionary * myDictionary1 = [ NSDictionary dictionaryWithObject : someObject forKey : @"key" ]; NSDictionary * myDictionary2 = [ NSDictionary dictionaryWithObjectsAndKeys : object1 , key1 , object2 , key2 , nil ]; NSNumber * myNumber = [ NSNumber numberWithInt : myInt ]; NSNumber * mySumNumber = [ NSNumber numberWithInt : ( 2 + 3 )]; NSNumber * myBoolNumber = [ NSNumber numberWithBool : YES ];
Пример с литералами:
NSArray * myArray = @[ object1 , object2 , object3 ] ; NSDictionary * myDictionary1 = @{ @"key" : someObject } ; NSDictionary * myDictionary2 = @{ key1 : object1 , key2 : object2 } ; NSNumber * myNumber = @( myInt ) ; NSNumber * mySumNumber = @( 2 + 3 ) ; NSNumber * myBoolNumber = @YES ; NSNumber * myIntegerNumber = @8 ;
Однако, в отличие от строковых литералов , которые компилируются в константы в исполняемом файле, эти литералы компилируются в код, эквивалентный вызовам методов выше. В частности, при ручном управлении памятью с подсчетом ссылок эти объекты автоматически освобождаются, что требует дополнительной осторожности, например, при использовании с переменными-статиками функций или другими видами глобальных переменных.
При использовании компилятора Apple LLVMNSArray
4.0 или более поздней версии, массивами и словарями ( и NSDictionary
классами) можно манипулировать с помощью подписки. [46] Подписку можно использовать для извлечения значений из индексов (массив) или ключей (словарь), а с изменяемыми объектами ее также можно использовать для установки объектов в индексы или ключи. В коде подписка представлена с помощью скобок [ ]
. [48]
Пример без подписки:
id object1 = [ someArray objectAtIndex : 0 ]; id object2 = [ someDictionary objectForKey : @"key" ]; [ someMutableArray replaceObjectAtIndex : 0 withObject : object3 ]; [ someMutableDictionary setObject : object4 forKey : @"key" ];
Пример с подпиской:
идентификатор объекта1 = someArray [ 0 ]; идентификатор объекта2 = someDictionary [ @"key" ]; someMutableArray [ 0 ] = object3 ; someMutableDictionary [ @"key" ] = object4 ;
После покупки NeXT компанией Apple были предприняты попытки сделать язык более приемлемым для программистов, более знакомых с Java, чем со Smalltalk. Одной из таких попыток было введение того, что в то время называлось «современным синтаксисом» для Objective-C [49] (в отличие от текущего «классического» синтаксиса). Никаких изменений в поведении не произошло, это был просто альтернативный синтаксис. Вместо того, чтобы писать вызов метода вроде:
объект = [[ MyClass alloc ] init ]; [ объект firstLabel : param1 secondLabel : param2 ];
Вместо этого он был написан как
объект = ( MyClass.alloc ) .init ; объект.labels ( param1 , param2 ) ;
Аналогично, декларации вышли из формы
- ( void ) firstLabel : ( int ) param1 secondLabel : ( int ) param2 ;
к
- ( void ) метки ( int param1 , int param2 );
Этот «современный» синтаксис больше не поддерживается в текущих диалектах языка Objective-C.
Проект mulle-objc — это еще одна повторная реализация Objective-C. Он поддерживает компиляторы GCC или Clang / LLVM в качестве бэкэндов. Он отличается от других сред выполнения с точки зрения синтаксиса, семантики и совместимости с ABI. Он поддерживает Linux, FreeBSD и Windows.
Помимо реализации GCC / NeXT / Apple , которая добавила несколько расширений к оригинальной реализации Stepstone , существует еще одна бесплатная реализация Objective-C с открытым исходным кодом, называемая Portable Object Compiler. [50] Набор расширений, реализованных Portable Object Compiler, отличается от реализации GCC/NeXT/Apple; в частности, он включает блоки, подобные Smalltalk , для Objective-C, при этом в нем отсутствуют протоколы и категории — две функции, широко используемые в OpenStep и его производных и родственных языках. В целом, POC представляет собой более старую стадию в развитии языка, предшествующую NeXT, примерно соответствующую книге Брэда Кокса 1991 года.
Он также включает в себя библиотеку времени выполнения ObjectPak, которая основана на оригинальной библиотеке Кокса ICPak101 (которая, в свою очередь, является производной от библиотеки классов Smalltalk-80) и радикально отличается от OpenStep FoundationKit.
Система ПК GEOS использовала язык программирования, известный как GEOS Objective-C или goc ; [51] несмотря на схожесть названий, эти два языка похожи только общей концепцией и использованием ключевых слов с префиксом @.
Компиляторный набор Clang , часть проекта LLVM , реализует Objective-C и другие языки. После того, как GCC 4.3 (2008) перешел на GPLv3, Apple отказалась от него в пользу clang, компилятора, который она имеет больше прав на изменение. В результате многие из современных функций языка Objective-C поддерживаются только Clang.
Схема версионирования Apple для своего "компилятора LLVM" на основе clang отличается от версионирования LLVM с открытым исходным кодом. См. Xcode § Toolchain версии для перевода [43]
Проект GNU долгое время интересовался платформой для переноса программ NeXT и Obj-C. ChangeLog для каталога libobjc в GCC предполагает, что он существовал до 1998 года (GCC 2.95), а его README дополнительно указывает на переписывание в 1993 году (GCC 2.4). [52]
Исходный код интерфейса NeXT был опубликован, поскольку он был создан как часть GCC, выпущенной GNU General Public License , которая обязывает тех, кто создает производные работы, делать это. [ когда? ] Apple продолжила эту традицию, выпустив свой форк GCC до версии 4.2.1, после чего они отказались от компилятора. Сопровождающие GCC приняли изменения, но не вложили много средств в поддержку новых функций, таких как язык Objective-C 2.0. [32] : Какой компилятор
Разработчики GNUstep, заинтересованные в новом языке, в 2009 году разделили GCC libobjc на независимый от GCC проект под названием libobjc2 . Они также договорились об использовании среды выполнения с Clang, чтобы воспользоваться преимуществами нового синтаксиса языка. [32] : Какой компилятор GCC медленно продвигался в то же время, но в GCC 4.6.0 (2011) они также перешли на Objective-C 2.0 в своем libobjc. [31] [53] Документация GNUstep предполагает, что реализация GCC по-прежнему не поддерживает блоки, нехрупкие переменные и более новый ARC. [32] : Какой исполняемый
В 2015 году Microsoft разделила libobjc2 на часть WinObjC , моста iOS для универсальной платформы Windows . В сочетании с собственной реализацией Cocoa Touch и базовыми API проект позволяет повторно использовать код приложения iOS внутри приложений UWP. [54]
В Windows инструменты разработки Objective-C доступны для загрузки на веб-сайте GNUStep. Система разработки GNUStep состоит из следующих пакетов: GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (похож на Xcode, но не такой сложный), Gorm (Interface Builder, похожий на Xcode NIB builder). Эти бинарные установщики не обновлялись с 2016 года, [55], поэтому лучшей идеей может быть просто установить, собрав под Cygwin или MSYS2 .
Objective-C сегодня часто используется в тандеме с фиксированной библиотекой стандартных объектов (часто называемой «kit» или «framework»), такой как Cocoa , GNUstep или ObjFW. Эти библиотеки часто поставляются с операционной системой: библиотеки GNUstep часто поставляются с дистрибутивами на основе Linux , а Cocoa — с macOS. Программисту не обязательно наследовать функции от существующего базового класса (NSObject – OFObject). Objective-C позволяет объявлять новые корневые классы, которые не наследуют ни одной существующей функции. Первоначально среды программирования на основе Objective-C обычно предлагали класс Object в качестве базового класса, от которого наследовались почти все остальные классы. С появлением OpenStep компания NeXT создала новый базовый класс с именем NSObject, который предлагал дополнительные возможности по сравнению с Object (например, акцент на использовании ссылок на объекты и подсчета ссылок вместо необработанных указателей). Почти все классы в Cocoa наследуются от NSObject.
Переименование не только служило для дифференциации нового поведения классов по умолчанию в API OpenStep, но и позволяло коду, который использовал Object — исходный базовый класс, используемый в NeXTSTEP (и, более или менее, в других библиотеках классов Objective-C), — сосуществовать в одной среде выполнения с кодом, который использовал NSObject (с некоторыми ограничениями). Введение двухбуквенного префикса также стало упрощенной формой пространств имен, чего нет в Objective-C. Использование префикса для создания неформального идентификатора упаковки стало неформальным стандартом кодирования в сообществе Objective-C и продолжается по сей день.
Совсем недавно начали появляться менеджеры пакетов, такие как CocoaPods , который стремится быть и менеджером пакетов, и репозиторием пакетов. Множество открытого кода Objective-C, написанного за последние несколько лет, теперь можно установить с помощью CocoaPods.
Реализации Objective-C используют тонкую систему времени выполнения, написанную на C [ требуется ссылка ] , что немного увеличивает размер приложения. Напротив, большинство объектно-ориентированных систем в то время, когда они были созданы, использовали большие среды выполнения виртуальных машин . Программы, написанные на Objective-C, как правило, не намного больше размера их кода и размера библиотек (которые, как правило, не нужно включать в дистрибутив программного обеспечения), в отличие от систем Smalltalk, где большой объем памяти использовался только для открытия окна. Приложения Objective-C, как правило, больше, чем аналогичные приложения C или C++, потому что динамическая типизация Objective-C не позволяет удалять или встраивать методы. Поскольку программист имеет такую свободу делегировать, пересылать вызовы, создавать селекторы на лету и передавать их в систему времени выполнения, компилятор Objective-C не может предполагать, что безопасно удалять неиспользуемые методы или встраивать вызовы.
Аналогично, язык может быть реализован поверх существующих компиляторов C (в GCC , сначала как препроцессор, затем как модуль), а не как новый компилятор. Это позволяет Objective-C использовать огромную существующую коллекцию кода C, библиотек, инструментов и т. д. Существующие библиотеки C могут быть обернуты в оболочки Objective-C для предоставления интерфейса в стиле OO. В этом аспекте он похож на библиотеку GObject и язык Vala , которые широко используются при разработке приложений GTK .
Все эти практические изменения снизили барьер для входа , что, вероятно, было самой большой проблемой для широкого распространения Smalltalk в 1980-х годах.
Распространенной критикой является то, что Objective-C не поддерживает пространства имен . Вместо этого программисты вынуждены добавлять префиксы к именам своих классов, которые традиционно короче имен пространств имен и, таким образом, более подвержены конфликтам. Начиная с 2007 года все классы и функции macOS в среде программирования Cocoa имеют префикс «NS» (например, NSObject, NSButton), чтобы идентифицировать их как принадлежащие ядру macOS или iOS; «NS» происходит от имен классов, определенных во время разработки NeXTSTEP .
Поскольку Objective-C является строгим надмножеством C, он не рассматривает примитивные типы C как объекты первого класса .
В отличие от C++ , Objective-C не поддерживает перегрузку операторов . Также в отличие от C++, Objective-C позволяет объекту напрямую наследовать только от одного класса (запрещая множественное наследование ). Однако в большинстве случаев категории и протоколы могут использоваться как альтернативные способы достижения тех же результатов.
Поскольку Objective-C использует динамическую типизацию во время выполнения и поскольку все вызовы методов являются вызовами функций (или, в некоторых случаях, системными вызовами), многие общие оптимизации производительности не могут быть применены к методам Objective-C (например: встраивание, распространение констант, межпроцедурная оптимизация и скалярная замена агрегатов). Это ограничивает производительность абстракций Objective-C относительно аналогичных абстракций в таких языках, как C++, где такие оптимизации возможны.
Первые версии Objective-C не поддерживали сборку мусора . В то время это решение было предметом некоторых споров, и многие люди считали, что длительные «мертвые времена» (когда Smalltalk выполнял сборку) делают всю систему непригодной для использования. Некоторые сторонние реализации добавили эту функцию (в частности, GNUstep с использованием Boehm ), а Apple реализовала ее в Mac OS X v10.5 . [56] Однако в более поздних версиях macOS и iOS сборка мусора была устарела в пользу автоматического подсчета ссылок (ARC), представленного в 2011 году.
С помощью ARC компилятор автоматически вставляет вызовы keep и release в код Objective-C на основе статического анализа кода . Автоматизация освобождает программиста от необходимости писать код управления памятью. ARC также добавляет слабые ссылки на язык Objective-C. [57]
Проектирование и реализация C++ и Objective-C представляют собой принципиально разные подходы к расширению C.
В дополнение к стилю процедурного программирования C, C++ напрямую поддерживает определенные формы объектно-ориентированного программирования , обобщенного программирования и метапрограммирования . C++ также поставляется с большой стандартной библиотекой , которая включает несколько классов-контейнеров . Аналогично, Objective-C добавляет объектно-ориентированное программирование , динамическую типизацию и рефлексию в C. Objective-C не предоставляет стандартную библиотеку как таковую , но в большинстве мест, где используется Objective-C, он используется с библиотекой, похожей на OpenStep, такой как OPENSTEP , Cocoa или GNUstep , которая предоставляет функции, аналогичные стандартной библиотеке C++.
Одно из заметных отличий заключается в том, что Objective-C обеспечивает поддержку времени выполнения для функций рефлексивного программирования , тогда как C++ добавляет лишь небольшое количество поддержки времени выполнения к C. В Objective-C объект может быть опрошен о его собственных свойствах, например, будет ли он отвечать на определенное сообщение. В C++ это невозможно без использования внешних библиотек.
Использование рефлексии является частью более широкого различия между динамическими (время выполнения) и статическими (время компиляции) функциями языка. Хотя Objective-C и C++ используют смесь обеих функций, Objective-C отдает предпочтение решениям во время выполнения, а C++ отдает предпочтение решениям во время компиляции. Напряжение между динамическим и статическим программированием включает в себя множество классических компромиссов в программировании: динамические функции добавляют гибкости, статические функции добавляют скорости и проверки типов.
Обобщенное программирование и метапрограммирование могут быть реализованы в обоих языках с использованием полиморфизма времени выполнения ( динамическая диспетчеризация ). В C++ это принимает форму виртуальных функций и идентификации типа времени выполнения , в то время как Objective-C предлагает динамическую типизацию и отражение. И Objective-C, и C++ поддерживают полиморфизм времени компиляции ( обобщенные функции ), а Objective-C добавил эту функцию в 2015 году.
Язык Swift является продуктом неустанных усилий команды экспертов по языку, гуру документации, ниндзя оптимизации компиляторов и невероятно важной внутренней группы по доводке, которая предоставляла обратную связь, чтобы помочь отточить и проверить идеи в бою. Конечно, он также значительно выиграл от опыта, полученного с трудом многими другими языками в этой области, черпая идеи из Objective-C, Rust,
Haskell
,
Ruby
,
Python
,
C#
, CLU и слишком многих других, чтобы перечислить их.
проблема возникла, когда NeXT предложила распространять модифицированный GCC двумя частями и позволить пользователю связывать их. Джобс спросил меня, законно ли это. В то время мне казалось, что это так, следуя рассуждениям вроде того, что вы используете; но поскольку результат был очень нежелательным для свободного программного обеспечения, я сказал, что мне придется спросить юриста. То, что сказал юрист, удивило меня; он сказал, что судьи сочтут такие схемы "уловками" и будут очень суровы по отношению к ним. Он сказал, что судья спросит, является ли это "действительно" одной программой, а не как она маркирована. Поэтому я вернулся к Джобсу и сказал, что мы считаем, что его план не разрешен GPL. Прямым результатом этого является то, что теперь у нас есть фронтенд Objective-C. Они хотели распространять парсер Objective C как отдельный фирменный пакет для связи с бэкэндом GCC, но поскольку я не согласился с тем, что это разрешено, они сделали его бесплатным.
-C является строгим надмножеством ANSI C
-C — это объектно-ориентированное строгое надмножество языка C
-C — это надмножество C
программирования Objective-C является надмножеством языка программирования C
Расширение .m изначально означало «сообщения», когда Objective-C был впервые представлен, ссылаясь на центральную функцию Objective-C.
данный момент разумно версионировать этот документ по выпускам его единственной реализации (и ее основного проекта), clang. "LLVM XY" относится к выпуску clang с открытым исходным кодом из проекта LLVM. "Apple XY" относится к предоставленному Apple выпуску компилятора Apple LLVM.
выполнения была полностью переписана в gcc 2.4. Более ранняя среда выполнения имела несколько серьезных ошибок и была довольно неполной.