Movatterモバイル変換


[0]ホーム

URL:


steps3D

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

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

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

При этом такой язык должен быть достаточно простым и основанным на языке С, чтобы облегчить переход разработчиков на него.

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

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

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

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

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

Привязка сообщения к соответствующей функции происходит непосредственно на этапе выполнения.

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

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

На данный момент языкObjective-C поддерживается компиляторомgcc (соответственно дляфорточек он поддерживаетсяmingw иcygwin). Судя по заявлениям в новой версииMac OS X 10.5будет кромеgcc (используемого для всех предыдущих версий) еще и компилятор от Intel'а. Также по слухам в этой версииоперационной системы будет использованObjective-C 2, куда в частности войдет поддержка сборки мусора(garbage collection)/

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

Эти двеruntime-библиотеки довольно похожи (в основном отличие заключается в именах методов), хотя далее я будуориентироваться наruntime-библиотеку от компании Apple.

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

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

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

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

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

   [receiver message];

В этой конструкцииreceiver является указателем на объект, аmessage - именемметода.

В отличии от языка С++ посылка сообщенияnil'у является законной операцией, всегда возвращающейнулевое значение (nil).

Сообщение может также содержать параметры:

   [myRect setOrigin:30.0 :50.0];

В этом примере именем метода (сообщения) являетсяsetOrigin::. Обратите внимание, что каждомупередаваемому аргументу соответствует ровно одно двоеточие. При этом в приведенном примере первыйаргумент имеет метку (текст перед двоеточием), а второй - нет.

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

   [myRect setWidth:10.0 height:20.0];

В этом примере в качестве имени сообщения выступаетsetWidth:height:.

Также поддерживается возможность передачи произвольного количества аргументов в сообщении:

   [myObject makeGroup: obj1, obj2, obj3, obj4, nil];

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

   float area = [myRect area];

Результат одного сообщения можно сразу же использовать в другом сообщении:

   [myRect setColor:[otherRect color]];

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

При этом само имя класса играет двойную роль - с одной стороны оно выступает как тип данных (т.е. онможет быть использован для описания указателей на объекты данного класса). А с другой стороныимя класса может выступать в качестве объекта, которому посылается сообщение (в сообщениях имя класса может принимать участиетолько какreceiver).

   Rect * myRect = [[Rect alloc] init]];

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

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

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

С одним из таких классов -NSString - мы столкнемся в данной статье. Этот класс служит дляработы со строками (при этом в качестве внутреннего представления символов используется Юникод).

Компилятор поддерживает данный тип, автоматически переводя конструкции вида @"my string" в указатель наобъект классаNSString, содержащий данную строку (точнее его подкласса, соответствующего константным строкам).

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

Все новые директивы компилятору в языкеObjective-C начинаются с символа @.

Как и в С++ описание класса и его реализация разделены (обычно описание помещается в заголовочныефайлы с расширениемh, а реализации - в файлы с расширениемm).

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

@interface ClassName : SuperClass{    instance variable declarations}method declarations@end

В версииruntime от Apple все классы имеют общего предка - классNSObject, содержащийцелый ряд важных методов.

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

@interface Rect : NSObject{float     width;float     height;BOOL      isFilled;NSColor * color;}@end

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

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

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

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

@interface Rect : NSObject{float     x, y;float     width;float     height;BOOL      isFilled;NSColor * color;}+ newRect;- (void) display;- (float) width;- (float) height;- (float) area;- (void) setWidth: (float) theWidth;- (void) setHeight: (float) theHeight;- (void) setX: (float) theX y: (float) theY;@end

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

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

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

ЯзыкObjective-C позволяет для аргументов метода задавать также один из следующих описателей -oneway,in,out,inout,bycopy иbyref. Данные описателислужат для задания направления передачи данных и способа передачи. Их наличие заметно упрощаетреализацию и работу с распределенными объектами (которые были реализованы в операционной системеNextStep кначалу 90-х годов прошлого века).

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

- makeGroup: (id) object, ...;

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

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

В этом случае можно воспользоваться директивой@class, объявляющей, что следующие за нейимена являются именами классов.

@class Shape, Rect, Oval;

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

#import "ClassName.h"@implmentation ClassName    method implementations@end

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

#import "Rect.h"@implmentation Rect+ newRect{Rect * rect = [[Rect alloc] init];[rect setWidth:  1.0f];[rect setHeight: 1.0f];[rect setX: 0.0f y: 0.0f];}- (float) width{   return width;}- (float) height{   return height;}- (float) area{   return [self width] * [self height];}- (void) setWidth: (float) theWidth{   width = theWidth;}- (void) setHeight: (float) theHeight{   height = theHeight;}- (void) setX: (float) theX y: (float) theY{   x = theX;   y = theY;}@end

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

@interface Worker : NSObject{char * name;@privateint    age;char * evaluation;@protectedint    job;float  wage;@publicid     boss}

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

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

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

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

Каждый объектObjective-C содержит в себе атрибутisa - указатель наclass object для данного объекта.class object автоматически создается компилятором исуществует как один экземпляр, на который черезisa ссылаются все экземпляры данного класса.

Каждыйclass object обязательно содержит в себе указатель наclass object дляродительского класса (superclass) иdispatch table. Последняя представляет из себясловарь, сопоставляющий селекторам сообщений фактические адреса реализующих их методов (функций).

Т.о. функцияobjc_msgSend ищет метод с данным селектором вdispatch table для данногообъекта. Если его там нет, то поиск продолжается вdispatch table для его родительского классаи т.д.

shapes class diagram

Рис 1. Диаграмма для классов (со следующим порядком наследованияRectangle:Shape:NSObject)

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

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

Если объект поддерживаетforwardInvocation:, то он может либо сам обработать посылаемоесообщение, либо переслать другому объекту для обработки:

- (void)forwardInvocation:(NSInvocation *)anInvocation{    if ( [someOtherObject respondsToSelector: [anInvocation selector]] )        [anInvocation invokeWithTarget: someOtherObject];    else       ..........}

Для ускорения поиска сообщений поdispatch table используется кэширование, позволяющеезаметно снизить затраты на пересылку сообщений. Стоит заметить, что языкObjective-Cактивно применялся для построения пользовательского интерфейса и библиотеки классов для операционнойсистемыNextStep, в то время когда эта система работа на компьютерах с 25-Мгц процессорамиMotorol 68030.

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

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

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

Так для получения селектора сообщения непосредственно по имени служит конструкция@selector():

   SEL setWidth = @selector(setWidth:);   SEL setPos   = @selector(setX:y:);

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

   SEL        setWidth   = NSSelectorFromString ( @"setWidth:" );   NSString * methodName = NSStringForSelector  ( setPos );

Мощная поддержка метаинформации вObjective-C позволяет прямо на этапе выполнения проверитьподдерживает ли объект метод с данным селектором при помощи посылки ему сообщенияrespondsToSelector::

    if ( [anObject respondsToSelector: @selector(setWidth:)] )         [anObject setWidth: 200.0];

Довольно легко можно послать сообщение, соответствующее данному селектору (без аргументов, с одним,двумя или тремя аргументами) при помощи методаperformSelector:,performSelector:withObject:,performSelector:withObject:withObject: иperformSelector::withObject:withObject::withObject:.

    [myObject performSelector:sel withObject: nil];

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

Можно получить класс для данного объекта, послав ему сообщениеclass. Это сообщениевозвращает класс в виде указателя на объект типаClass.

    Class    * cls     = [anObject class];    NSString * clsName = NSStringFromClass ( cls );

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

    Class * cls = NSClassFromString ( clsName );

Каждый метод фактически представляет собой функцию с двумя невидимыми аргументами -self и_cmd.

Первый является аналогомthis, т.е. указывает на сам объект - получатель сообщения.Второй - содержит селектор данного метода.

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

- (float) area{   return [self width] * [self height];}

Однако кромеself есть еще одна величина, которой могут посылаться сообщения -super.На самом делеsuper не является нормальной переменной - это всего лишь еще одно обозначениедля указателя на текущий объект. Но при посылке сообщенияsuper поиск метода начинается не сdispatch tableтекущего объекта, а сdispatch table родительского объекта.

sending messages to self and super

Рис. 2. Разница при посылке сообщенияself иsuper (для объекта классаRectangle).

Таким образом, посылая сообщенияsuper мы тем самым вызываем старые версии методов,переопределенные данным классом.

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

Такая функция отличается от описания метода только вставкой в начала списка аргументов двух дополнительных параметров - указателяна сам объект (self) и селектора данного метода (_cmd).

Послав объекту сообщениеmethodForSelector: мы получаем в ответ адрес реализующей этот методфункции.

typedef float (*WidthFunc)( id, SEL );typedef void  (*SetWidthFunc)( id, SEL, float );WidthFunc    widthFunc    = (WidthFunc)    [myRect methodForSelector: @selector (width)];SetWidthFunc setWidthFunc = (SetWidthFunc) [myRect methodForSelector: @selector (setWidth:)];(*setWidthFunc)( myRect, @selector (setWidth:), 27.5f );

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

Протоколы

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

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

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

@protocol ProtocolNamemethod declarations@end

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

@protocol Serializable- (id)   initWithCoder: (NSCoder *) coder;- (void) encodeWithCoder: (NSCoder *) coder;@end

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

@protocol MyProto <Protocol1, Protocol2, Serializable,Drawable>

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

@interface MyClass : SuperClass <Protocol1, Protocol2, Serializable,Drawable>

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

if ( [myObject conformsToProtocol: @protocol (Serializable)] )     [myObject encodeWithCoder: myCoder];

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

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

id <Serializable,Drawable>  myObject;

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

Shape <Serializable>  myObject;

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

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

Protocol * myProto = @protocol ( Serializable );

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

@protocol MyProto, Serializable, Drawable;

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

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

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

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

Cup * cup = [[Cup alloc] init];@try{    [cup fill];}@catch ( NSException * exc ){   NSLog ( @"Exception caught: %@", exc );}@catch ( id ex ){   NSLog ( @"Unknown exception caught" );}@finally{   [cup release];}

Для запуска исключения используется директива@throw, в качестве аргумента берущая указательна объект-исключение. Обычно вMac OS X/NextStep для этой цели используются объекты классаNSException.

NSException * exc = [NSException exceptionWithName: @"my-exception" reason: @"unknown-error"                                 userInfo: nil];@throw exc;

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

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

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

@synchronized () берет на вход указатель на объект языкаObjective-C (можно использоватьдля этой цели любой объект, в том числе иself), который играет роль мьютекса(mutex).

При попытке нити начать выполнение защищенного фрагмента проверяется не выполняется ли уже этот фрагменткакой-либо нитью. Если да, то сравниваются объекты, переданные этими нитями в@synchronized ().

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

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

- (void) criticalMethod{    @synchronized ( self )    {         // perfrom modifications to shared objects         . . .    }}

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

В самом языкеObjective-C нет специальных команд для создания и уничтожения объектов(подобныхnew иdelete). Эта задача ложится наruntime-библиотеку и реализуетсяпри помощи механизма посылки сообщений.

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

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

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

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

id anObject = [[Rectangle alloc] init];

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

id anObject = [Rectangle alloc];[anObject init];

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

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

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

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

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

Довольно часто у объектов бывает сразу несколько методов, начинающихся сinit, напримерinit,initWithName:,initWIthContentsOfFile: и т.д.

Установившейся практикой в таком случае является выделение среди всехinit-методов одного,называемогоdesignated initializer. Все остальныеinit-методы должны вызывать его итолько он вызывает унаследованныйinit метод.

- initWithName: (const char *) theName   // designated initializer{    [super init];                        // call inherited method    name = strdup ( theName );}- init{    return [self initWithName: ""];}

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

+ (NSString *) initStringWithCString: (const char *) str;+ (NSString *) initStringWithFormat: (NSString *) format, ...;

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

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

Установившейся практикой является посылка объекту сообщенияretain всеми, заинтересованнымив нем сторонами (объектами), т.е. если вы запоминаете ссылку на объект, то следует послать емусообщениеretain.

Когда объект перестает быть нужен, то ему просто посылается сообщениеrelease.

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

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

- (void) dealloc{    . . .    [super dealloc];}

Категории

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

Так можно добавить новый метод классуNSObject и этот метод автоматические добавится во всеостальные классы !

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

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

Категория имеет свое имя, список методов и имя класса, который она расширяет. Описание категорииимеет следующий вид:

#import "ClassName.h"@interface ClassName ( CategoryName )  methods declarations@end

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

#import "CategoryName.h"@interface ClassName ( CategoryName )  methods bodies@end

Class objects иObjective-C runtime

При компиляции программы наObjective-C компилятор для каждого введенного класса автоматическисоздает так называемыйclass object - полноценный объект, содержащий в себе всю информацию оданном классе, включая название, суперкласс, список методов иinstance-переменных.

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

Одной из особенностьюclass object'а является то, что он поддерживает все методы классаNSObject, т.е. когда ему посылается сообщение, то сначала идет поиск среди методов класса, а если методне найден, то поиск продолжается средиinstance-методов классаNSObject.

Еще одной интересной особенностью является возможность инициализацииclass object'ов - в началеработы приложения каждомуclass object'у посылается сообщение (класса)initialize.

Это сообщение гарантированно посылается каждомуclass object'у, причем всего один раз и дотого, как ему будет послано любое другое сообщение. Простейшим примером применения такого сообщенияявляется реализацияSingleton'ов - именно в методеinitialize следует создать тот самыйединственный экземпляр объекта и запомнить его вstatic-переменной.

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

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

Method   class_getInstanceMethod( Class aClass, SEL aSelector );Method   class_getClassMethod   ( Class aClass, SEL aSelector );struct objc_method_list * class_nextMethodList(Class theClass, void ** iterator);void     class_addMethods       ( Class aClass, struct objc_method_list * methodList );void     class_removeMethods    ( Class aClass, struct objc_method_list * methodList );unsigned method_getNumberOfArguments ( Method method );unsigned method_getSizeOfArguments   ( Method method );unsigned method_getArgumentInfo      ( Method method, int argIndex, const                                       char ** type, int *  offset );Ivar     class_getInstanceVariable   ( Class aClass, const char * aVariableName );

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

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

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

    void                    * iterator = 0;    struct objc_method_list * mlist;    //    // Each call to class_nextMethodList returns one methodList    //    methodList = class_nextMethodList( classObject, &iterator  )    while ( methodList != NULL )    {        // …do something with the method list here…        methodList = class_nextMethodList ( classObject, &iterator  );    }

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

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

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

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

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

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

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

char * buf1 = @encode ( int ** );char * buf2 = @encode ( struct key );char * buf3 = @encode ( Rectangle );

Objective-C 2.0

В Mac OS X 10.5 Leopard вошла новая вресия языкы - Objective-C 2.0, в которую было добавлено много элементов, упрощающих использование языка. В частности именно эта версия (2.0) используется для разработки приложений под платформуiPhone/iPod Touch.

Одним из наиболее значительный изменений является опциональная поддержка сборки мусора (garbage collection). Для подключения этой возможности достаточно просто задать в свойствах проекта флаг использования сборки мусора. Т.е. это необязательная опция - каждый сам может решать стоит ли ему ее использовать или нет. Кроме того, любое корректно написанное приложение (используующее библиотеки Apple), работающее безgarbage collection, будучи откомпилированным с этой опцией, также будет полностью корректно работать. Следует правда отметить, что для разработки приложений под платформу iPhone/iPod Touchgarbage collection пока не поддерживается.

Также в Objective-C 2.0 вошел более привычный синтаксис для доступа к свойствмм (properties) объектов, используя точкукак для доступа к полям структуры в C/C++/Java.

Пусть у нас есть класс Person, описанный ниже.

#import <Cocoa/Cocoa.h>@interface Person : NSObject{    NSString * firstName;    NSString * lastName;}- (NSString *) firstName;- (void) setFirstName: (NSString *) newFirstName;- (NSString *) lastName;- (void) setLastName: (NSString *) newFirstName; @end

Тогда, если у нас переменнаяbill является указателем на объект класс Person, то следующие конструкции для доступак полям (свойствам) этого объекта полностью законны.

NSLog ( @"First name is %@", bill.firstName );bill.firstName = @"Bill"bill.lastName  = @"Gates";

При этом на самом деле подобный синтаксис является всего лишьsyntax sugar и переводится компиляторм в нормальный с использованием set/get-методов:

NSLog ( @"First name is %@", [bill firstName] );[bill setFirstName: @"Bill]"[bill setLastName: @"Gates"];

Очень удобной возможностью Objective-C 2.0 стала поддержка автоматического создания set/get-методов. Реализации этих методовобычно крайне просты и однообразны, но отнимают время и силы программиста. Поэтому эта работа легко может бытьпереложена на компилятор.

Для этого необходимо в разделе@interface описать каждое свойство, методы доступа к которому должно быть созданы при помощи директивы@property, а затем в разделе@implmentation использовать директиву@synthesize или@dynamic.

Директива@property имеет следующую структуру:

@property (attributes) type name;

Черезtype иname обозначены тип значения и имя свойства. В круглых скобках задается список атрибутов, задающихколмпилятору какие именно set/get-методы нужно создать.

Доупстимы следующие атрибуты:

Обратите внимание, что опцииreadonly/readwrite являются взаимоисключающими. Также взаимоисключающими являетсяопцииassign/retain/copy.

Ниже приводятся реализации set-метода для каждого из трех опций -assign/retain/copy.

// assign- (void) setFirstName: (NSString *) newName{firstName = newName;}// retain- (void) setFirstName: (NSString *) newName{if ( firstName != newName ){[firstName release];firstName = [newName retain];}}// copy- (void) setFirstName: (NSString *) newName{if ( firstName != newName ){[firstName release];firstName = [newName copy];}}

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

С использованием директивы@property описание класса Person может выглядеть следующим образом:

#import <Cocoa/Cocoa.h>@interface Person : NSObject{    NSString * firstName;    NSString * lastName;}@property (nonatamic,retain) NSString * firstName;@property (nonatomic,retain) NSString * lastName; @end

Для создания реализации set/get-методов в разделе@implmentation следует использовать директиву@synthesize:

#import "Person.h"@implementation Person- (void) dealloc {    [firstName autorelease];    [lastName autorelease];}@synthesize firstName;@synthesize lastName;@end

Вместо директивы@synthesize можно использовать директиву@dynamic (по умолчнию), говорящую, что set/get-методы Вызададите сами.

Еще одним нововведением языка Objective-C 2.0 стало упрощенная форма цикла по содержимому контейнера. Так если у насarray - это объект класса, унаследованного от NSArray, аdict - объект класса, унаследованного от NSDictionary, то для перебора элементов в них можно использовать следующие фрагменты кода:

for ( NSString * str in array )    NSLog ( @"Element: %@", str );for ( id key in dict )    NSLog ( @"Key: %@ Value: %@", id, [dict valueForKey: key] );

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

Разное

Крайне полезным (и пожалуй главным) источником информации по языку является сайтdeveloper.apple.com.

Также ряд полезной информации по языкуObjective-C можно найти вnews-группеcomp.lang.objective-c.

ПроектGNUstep представляет собой попытку созданияopen-source библиотек наObjective-C, являющихся аналогом библиотекFoundationиAppKit вNextStep иMac OS X.

Весьма полезныйперевод статьи Objective-C для программистов C++.

На сайте проектаGNUstep Вы найдете много примеров использование языкаObjective-C и приложений,написанных на нем.

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

Для работы сObjective-C подM$ Windows можно использовать компиляторыmingw иcygwin, необходимо только установить библиотеку для поддержки языка.

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

Официальныйcomp.lang.objective-C FAQ.

В работе над статьей были использованы материалы сdeveloper.apple.com.

Valid HTML 4.01 Transitional

Напиши мне
Используются технологииuCoz

[8]ページ先頭

©2009-2025 Movatter.jp