Objective-C для программистов C++. Часть 2

Написал Vanger, в Статьи » C&C++.
Перевод серии статей David Chisnall «Objective-C for C++ Programmers»
http://netsago.org/ru/docs/1/15/
Objective-C был спроектирован как минимальный набор расширений для C, предоставляющих семантику, схожую со Smalltalk. Он также включает в себя схожий со Smalltalk синтаксис, с помощью которого проще увидеть, какая часть кода является чистым C, а какая — содержит расширения Objective-C.

Классы не являются особенными

В Smalltalk классы — это просто объекты с некоторыми специальными возможностями. То же самое справедливо и в Objective-C. Класс — это объект. Он отвечает на сообщения так же, как и объект. И Objective-C, и C++ разделяют выделение памяти для объекта и его инициализацию:
• В C++ выделение памяти для объекта делается с помощью оператора new. В Objective-C это делается отправлением классу сообщения alloc, который, в свою очередь, вызывает malloc() или аналог.
• Инициализация в C++ происходит с помощью вызова функции с именем, аналогичным имени класса. Objective-C не проводит различий между методами инициализации и другими методами, но по соглашению метод инициализации по умолчанию называется init.

Когда вы объявляете метод, на который отвечает объект, объявление начинается со знака «-», а «+» используется для методов класса. Обычно эти префиксы используются для сообщений в документации, так что вы должны писать +alloc и -init, чтобы указать, что alloc посылает сообщение классу, а init — экземпляру.

Классы в Objective-C, как и в других объектно-ориентированных языках, это «фабрики» объектов. Большинство классов сами не реализуют метод +alloc; вместо этого они наследуют его от супер-класса. В NSObject, базовом классе в большинстве программ на Objective-C, метод +alloc вызывает +allocWithZone. Он принимает NSZone в качестве аргумента, — C-структуру, содержащую некоторую линию поведения для выделения памяти под объекты. Возвращаясь в 1980-е, когда Objective-C использовался в NeXTSTEP для реализации драйверов устройств и большинства GUI на компьютерах с 8 МБ ОЗУ и ЦПУ с тактовой частотой 25 МГц, NSZone была очень важна для оптимизации. На данный момент, почти все программисты на Objective-C её игнорируют (у неё столько же шансов стать более полезной, как у архитектуры NUMA — более используемой).

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

Распространенный трюк реализации этой возможности называется «isa-swizzling». Как я уже сказал, объекты в Objective-C являются структурами C, где первый элемент — указатель на класс. К этому элементу можно получить доступ точно так же, как и к другим переменным экземпляра; вы можете изменить класс объекта в рантайме, просто присвоив новое значение. Конечно же, если вы установите класс объекта чему-то, что имеет другую структуру в памяти, это закончится ужасно. Тем не менее, у вас может быть суперкласс, которые определяет интерфейс, а затем набор подклассов, которые определяют поведение. Например, эта техника используется в стандартном строковом классе (NSString), в котором есть различные экземпляры для различных кодировок, для статических строк и т.д.

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

Типы и указатели

Objective-C официально не позволяет определять объекты в стеке. Хотя это не совсем правда, — возможно, определить объекты в стеке, но очень сложно сделать это корректно, так как нарушает соглашения об организации управления памятью. В результате, каждый объект в Objective-C — указатель. Некоторые типы определены Objective-C; они определены в заголовочных файлах как типы C.

Три наиболее часто используемых новых типов в Objective-C — это id, Class и SEL. id — это указатель на объект Objective-C. Это эквивалентно void* в C, к которому вы можете привести любой тип указателя на объект, а также привести его к любому другому типу указателя на объект. Вы можете попробовать отправить какое-нибудь сообщение к id, но вы получите исключение в рантайме, если такая возможность не поддерживается.

Class — это указатель на класс Objective-C. Классы являются объектами, поэтому они также могут получать сообщения. Имя класса является типом, а не переменной. Идетификатор NSObject это тип экземпляра NSObject, но он также может быть использован в качестве получателя сообщений. У вас может быть такой класс:

[NSObject class];



Этот код посылает сообщения +class классу NSObject, который возвращает указатель к структуре Class, представляющую собой сам класс. Это полезно для интроспекции, как мы увидим далее.

Третий тип, SEL, представляет собой селектор — абстрактное представление имени метода. Вы можете создать его во время компиляции с помощью директивы @selector() или в рантайме путем вызова функции рантайм библиотеки со строкой C или используя функцию OpenStep NSSelectorFromString(), которая дает селектор для строки Objective-C. Эта техника полхволяет вам вызывать методы по имени. Вы можете сделать это в C, используя что-то вроде dlsym(), но это гораздо сложнее в C++. В Objective-C вы можете сделать такое:

[object performSelector:@selector(doSomething)];



Что будет эквивалентно следующему:

[object doSomething];



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

В C++ нет эквивалента типу id, так как объекты всегда должны быть типизированы. В Objective-C у вас есть дополнительная система типов. Оба следующих варианта будут корректны:

id object = @"a string";

NSString *string = @"a string";



На самом деле, константная строка — это экземпляр класс NSConstantString, который является дочерним классом NSString. Присваивание его к NSString* включает проверку типов во время компиляции для сообщений и доступ к публичным переменным экземпляра (которые почти никогда не используются в Objective-C). Вы можете нарушить эту установку, сделав следующее:

NSArray *array = (NSArray*)string;



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

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

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

Определение классов в Objective-C есть секции интерфейса и реализации. В C++ есть нечто похожее, но там это в какой-то степени смешано. Интерфейс в Objective-C только определяет части, которые точно должны быть общедоступны. В целях реализации, он включает в себя приватные переменные экземпляра, так как вы не можете сделать класс суперклассом, пока не знаете, насколько он велик. Более поздние реализации, как, например, 64-битный рантайм от Apple, не имеет этого ограничения.

Интерфейс объектов в Objective-C выглядит следующим образом:

@interface AnObject : NSObject  {

@private

    int integerivar

@public

    id anotherObject;

}

+ (id) aClassMethod;

- (id) anInstanceMethod:(NSString*)aString with:(id)anObject

@end



Первая строка содержит три части: идентификатор AnObject — имя нового класса. Имя после двоеточия — это объект NSObject (это необязательно, но почти каждый объект в Objective-C расширяет NSObject). Имена в угловых скобках это протоколы, — это схоже с интерфейсами в Java — которые реализуются этим классом.

Как и в C++, переменные экземпляра («поля» в C++) могут иметь спецификаторы доступа. В отличии от C++, перед этими спецификаторами ставится знак «@», чтобы избежать совпадений с корректными идентификаторами C.

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

struct _AnObject

{

    @defs(AnObject);

};



Директива @defs означает: «Поместить все поля указанного объекта в эту структуру», поэтому структура _AnObject имеет точно такой же формат в памяти, как и экземпляр класса AnObject. Вы можете использовать это правило, например, для доступа к переменным экземпляра напрямую. Часто это используют, чтобы позволить функции C управлять объектом Objective-C напрямую, в целях повышения производительности.

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

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

Интерфейс схож с ранним объявлением в C. Ему так же нужна реализация, которая использует @implementation, что не удивительно:

@implementation AnObject

+ (id) aClassMethod

{

...

}

- (id) anInstanceMethod:(NSString*)aString with:(id)anObject

{

...

}

@end



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

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

Традиционно Objective-C не предоставляет каких-либо возможностей по управлению памятью. В ранних версиях корневой класс Object реализовывал метод +new, который вызывал malloc() для создания нового объекта. Когда вы заканчивали работу с объектом, вы посылали сообщение -free. OpenStep добавил подсчет ссылок. Каждый объект, наследуемый от NSObject, отвечает на сообщения -retain и -release. Когда вы хотите хранить указатель на объект, вы отправляете сообщение -retain. Когда вы заканчиваете работу, отправляете сообщение -release.

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

Решением этой проблемы стал класс NSAutoreleasePool. В дополнение к -retain и -release, NSObject также отвечает на сообщение -autorelease. Когда вы отправляете какое-либо из них, оно регистрирует себя с текущим пулом автоматического высвобождения. Когда объект пула уничтожен, отсылается сообщение -release к каждому объекту, который ранее получил сообщение -autorelease. В приложениях OpenStep экземпляр NSAutoreleasePool создается в начале каждого цикла и уничтожается в конце. Также вы можете создавать собственные экземпляры, чтобы освободить автоматически высвобождаемые объекты быстрее.

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

В Objective-C общий шаблон определяет неизменяемого класса, а затем изменяемого подкласса. NSString является типичным примером, — у него есть изменяемый подкласс NSMutableString. Если вы получаете NSString и хотите сохранить его, вы можете послать сообщение -retain и сохранить указатель без операции копирования. В качестве альтернативы вы можете послать NSString сообщение +stringWithString:. Он проверит, изменяемый ли аргумент и если да, вернет оригинальный указатель.

Objective-C 2.0 как с рантайм Apple, так и GNU, поддерживает не перемещающий сборщик мусора, который избавляет от необходимости пользоваться сообщениями -retain и -release. Это дополнение к языку это не всегда хорошо поддерживается существующими фреймворками и должно использоваться с осторожностью.
#1 ATR |  

Продвижение сайтов ростов. Профессиональное медиапланирование и продвижение google [url=http://gruppa-rostov.ru]тут[/url]. На лучших условиях.


Продвижение сайтов ростов. Профессиональное медиапланирование и продвижение google [url=http://gruppa-rostov.ru]тут[/url]. На лучших условиях.
Дата публикации: 17 июля 2010 14:30 | ICQ: --
цитировать

Добавить комментарий


Включите эту картинку для отображения кода безопасности
обновить код



 

Лучшие новости

Наш опрос

Мы в интернете


Профиль

Добро пожаловать, гость. Войдите, или зарегистрируйтесь.
Логин:

Пароль:

Забыли пароль?

Друзья


хомяк Немиро Алексея


Музыка микросхем
Хак-академия

Интересные ссылки


У нас есть темы для Windows xp. И не только.
Срочно требуется лечение геморроя народными средствами, чтобы избежать операции.