Несмотря на то, что для МакОСи можно создавать приложения на C/C++ или Java, Objective-C фактически стал стандартом при разработке под эту операционку. Это произошло благодаря удобному и функциональному API для этого языка — фреймворку Cocoa. Об ObjectiveC и Cocoa мы сегодня и поговорим.

 

C превращается… в C с объектами

Objective-C появился через год после плюсов, в 1986 году, благодаря работе Brad Cox и Tom Love в компании Stepstone. Они хотели соединить высокий уровень абстракции и повторного использования кода, характерные для объектно-ориентированного программирования, с производительностью и простотой синтаксиса языка С.

Для этого им пришлось модифицировать C, добавив некоторые фичи из Smalltalk, позволяющие работать с объектами. Получившееся объектно-ориентированное расширение языка С впоследствии стало носить название Objective-C — «объектный C». Чтобы понять, как этот продукт скрещивания двух древних языков программирования попал на современные маки и разнообразные мобильные устройства Apple, нужно вспомнить историю яблочной компании… Apple была основана в 1980 Стивом Джобсом и Стивом Возняком.

После многих лет успеха на посту главы компании Джобса сменил бывший управляющий компании Coca-Cola Джон Скалли (John Sculley). Стив покинул Apple и через некоторое время основал новую компанию, назвав ее NeXT. Среди основных задач NeXT было создание операционной системы нового поколения. Для ее разработки было решено использовать среди прочих средств и Оbjective-C — он стал основой для API. Вскоре операционка от NeXT была готова и получила название NeXTStep. Собственно, это и было первое серьезное применение ObjC. Позже NeXT объединила усилия с Sun Microsystems для создания следующей версии NeXTStep — OPENStep (в настоящий момент продолжает свое существование только GNUStep, поддерживаемая сообществом свободного программного обеспечения). В середине 1990-х Джон Скалли покинул Apple, которая стремительно теряла свои позиции на рынке. Для спасения ситуации компания решила создать новую операционную систему взамен устаревающей Mac OS. После нескольких неудачных попыток Apple решила купить компанию, которая уже имеет свою операционку. Этой компанией, как ты уже наверно догадался, стала NeXT. Так Джобс снова оказался в Apple, на основе NeXTStep была создана Mac OS X, а Objective-C занял свое место среди инструментов Mac-разработчика и в API Mac OS X.

 

Разговорчивые объекты

Итак, Objective-C — объектно-ориентированное расширение языка С. А что в нем, собственно, есть такого, чего нет в тех же плюсах? Среди основных достоинств Objective-C нужно перечислить следующие:

  • Синтаксис Objective-C очень прост. Чтобы его изучить, С-программисту понадобится всего пара дней. Он является именно расширением языка С, в него просто добавлены новые возможности для объектноориентированного программирования. Таким образом, абсолютно любая программа на С является программой и на Objective-C (что для С++, вообще-то, неверно).
  • Objective-C — message-oriented language, то есть объекты в нем общаются между собой не с помощью явного вызова инкапсулированных функций, как в C++ или Java, а при помощи отправки друг другу сообщений.

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

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

 

Здравствуй, мир!

Линус Торвальдс как-то сказал: «Разговоры — дешевка. Покажите мне код!». Показываю! Вот код HelloWorld , а на Obj-C. Кстати, если у тебя нет под рукой Mac OS X или хакинтоша, можешь попробовать в действии Objective-C и Cocoa c помощью GCC и проекта OpenStep.

Наша первая прога на ObjC

#import <Cocoa/Cocoa.h>
void main()
{
NSLog(@»Hello world!»);
}

Наш HelloWorld сильно напоминает C++, не так ли? Но есть и свои особенности, которые ты, конечно, сразу заметил.

  • #import — директива препроцессора, аналогичная #include, но, в отличие от последней, она не позволяет включить заголовок более одного раза, то есть работает так же, как стражи включения в C++ (#include, разумеется, тоже поддерживается).
  • Cocoa.h — заголовок, содержащий описания функций, классов и констант фреймворка Cocoa, о котором я говорил в начале.
  • NSLog — одна из таких функций. Она выводит текстовое сообщение в stdout вместе с timestamp’ом в начале строки. Помнишь NeXTStep? Префикс «NS» как раз оттуда :).

Строки в Objective-C — отдельная история. NSLog получает на вход не C-строку, а объект класса NSString. Заметил собаку перед строкой в NSLog? Это указание компилятору ObjC создать константу типа NSString — в Сocoa используются не zerro-terminated строки, как в C, а объекты класса NSString или его наследников (NSMutableString например). Создатели Cocoa позаботились о том, чтобы в NSString были реализованы все методы работы со строками, которые тебе чаще всего приходится использовать. Строки можно склеивать, разбивать по разделителю, искать в них подстроки, делать форматированный вывод и много чего другого.

На скриншоте ты можешь видеть код нашего простенького примера в среде XCode и результат его выполнения. XCode — это среда разработки, используемая при написании приложений для Mac OS и iOS. В ней довольно удобно работать, но, как и ко всему новому, к ней нужно привыкнуть. Кроме редактора и отладчика в нее интегрированы средства построения пользовательского интерфейса — «Interface Builder», управления версиями — «SCM» и еще множество всяких полезных фишек. В качестве компилятора используется допиленный Apple GCC. В качестве отладчика — GDB. В настоящий момент на сайте Apple свободно доступна для скачивания XCode 3, пробная версия XCode 4 доступна только для зарегистрированных Appleразработчиков. В общем, качай, устанавливай, пробуй…

 

Рабочий класс

Раз язык объектно-ориентированный, значит, в нем должны быть объекты. Давай посмотрим, как их создавать и использовать в Objective-C. Каждый класс в коде на Objective-C разделен на интерфейс и реализацию, которые принято хранить в отдельных файлах. Заголовочные файлы получают расширение «h», а файлы с реализацией — «m».

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

// Класс Dog — наследник NSObject
// Разместим его интерфейс в «Dog.h»
@interface Dog : NSObject
{
// Здесь размещаем поля объекта
@private
int Age;
@public
int Color;
}
// После этого идет описание сообщений,
// на которые отвечает объект.
// Перед сообщениями экземпляров ставится «-»,
// а перед сообщениями класса
// (аналог статических методов в C++) — знак «+».
// В описании сообщения указывается имя
// сообщения, список передаваемых параметров
// и тип возвращаемого значения.
- (void) voice;
- (void) setAge: (int) age;
- (int) getAge;
@end
// Реализация сообщений нашего класса (Dog.m)
@implementation Dog
- (void) voice
{
NSLog(@"Woof woof");
}
// Вообще, для этого есть свойства,
// но о них как-нибудь в следующий раз
- (void) setAge: (int) age
{
Age = age;
}
- (int) getAge
{
return Age;
}
@end

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

[my_class_pointer message_name: arg1 arg2_name: arg2 arg3_name: arg3]

Например:

[dog1 setAge: 3];

Немного непривычно после точек и стрелок C++ и Java, но к такому синтаксису быстро привыкаешь и даже начинаешь считать его удобным. Что интересно, мы можем посылать сообщения нулевому указателю (nil). Результат всегда будет nil.

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

Dog * dog1 = [[Dog alloc] init];

alloc — метод класса NSObject. Он выделит необходимое количество памяти под наш объект и вернет указатель на него. После этого мы можем выполнить инициализацию, послав только что созданному объекту сообщение init. Мы не переопределяли обработку сообщений alloc и init, поэтому будут использованы реализации из NSObject. На практике init часто приходится переопределять для того, чтобы произвести какую-то начальную настройку объекта или передать дополнительные параметры. init играет роль конструктора в ObjC.

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

int age = [dog1 getAge];
[dog1 voce];

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

[dog release];

Управление памятью реализовано в ObjC в виде подсчета ссылок. alloc создает объект с числом ссылок, равным единице. release уменьшает количество ссылок для данного объекта на 1. Объект будет уничтожен, когда счетчик достигнет 0. Если тебе когданибудь приходилось использовать COM, то такая реализация управления памятью должна быть тебе хорошо знакома.

Значение счетчика можно увеличивать, посылая объекту сообщение retain. Кстати, начиная с Objective-C 2.0, стала доступна сборка мусора, ее можно совмещать с классическим управлением памятью с помощью подсчета ссылок. Кроме явного уничтожения объекта, ты можешь добавить его в пул временных объектов (NSAutoreleasePool), послав сообщение autorelease. Так часто поступают, например, при возвращении объектов методы классов Cocoa (метод stringByAppendingString объектов NSString, например).

NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString *str;
// Проинициализируем строчку и добавим
// ее в pool
str = [[[NSString alloc] init] autorelease];
// ...
[pool drain]; // Здесь объект str будет уничтожен

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

Код нормального процесса исполнения и код обработки ошибок смешивается, что не есть хорошо. К счастью, в ObjC, как и в C++, есть механизм исключений.

Вот пример обработки исключительной ситуации в ObjC:

Обработка исключений в Objective-C

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

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

 

«Кокао» для кодера

После экскурса по Objective-C можно поговорить о доступных фреймворках МакОСи. Куда же без них? Фреймворк сделает за тебя всю грязную работу, чтобы ты мог спокойно заниматься основной задачей.

Для разработки нативных приложений под МакОСь распространение получили два фреймворка — Cocoa и Carbon. Если ты пишешь под Мак или под iOS на Objective-C, то твой выбор — Cocoa.

Cocoa — это продукт эволюции программных сред NeXTSTEP и OPENSTEP, которые разрабатывались компанией NeXT. Перекочевав на Mac OS X, фреймворк сильно изменился, и в нем появилось множество новых классов. Все они, как в С# или Java, являются наследниками одного класса — NSObject.

В Cocoa представлены базовые типы, такие как NSNumber и NSString, различные контейнеры (NSArray, NSDictionary), обертки для системных объектов и т.д. Давай-ка опробуем Cocoa и Objective-C в действии и создадим небольшое консольное Cocoa-приложение.
В Сети существует множество веб-сервисов. Некоторые из них просто предоставляют информацию в удобном формате, например, в XML.

Одним из таких сервисов является сервис Российского Центробанка, предоставляющий информацию о текущих курсах валют. По адресу www.cbr.ru/scripts/XML_daily.asp можно получить XML’ку следующего вида:

<ValCurs Date="22.09.2010" name="Foreign Currency
Market">
<Valute ID="R01010">
<NumCode>036</NumCode>
<CharCode>AUD</CharCode>
<Nominal>1</Nominal>
<Value>29,4185</Value>
</Valute>
...
<Valute ID="R01020A">
<NumCode>944</NumCode>
<CharCode>AZN</CharCode>
<Nominal>1</Nominal>
<Value>38,6777</Value>
</Valute>
</ValCurs>

Создадим класс ObjC RCBDayly, который будет скачивать эту XML’ку, парсить ее и предоставлять данные в удобном для нас виде. Исходник нашего класса ты можешь увидеть на врезке.

На этом примере ты можешь видеть, как легко в Cocoa работать с сетью и XML. При создании подобных приложений можно обойтись, конечно, и средствами POSIX, а вот если ты хочешь создать GUI-приложение, без Cocoa тебе не обойтись. Но об этом как-нибудь в другой раз.

 

Заключение

Разнообразные гаджеты от Apple производятся и продаются по всему миру в огромном количестве. Всех их объединяет то, что на них работает Mac OS X или iOS и, если ты хочешь освоить разработку под эти операционки, знания Objective-C тебе очень пригодятся. До встречи в эфире!

 

Исходник нашего XML-парсера

// Импортируем классы Cocoa
#import <Cocoa/Cocoa.h>
// Опишем интерфейс нашего класса
@interface RCBDayly : NSObject
{
@private
// NSMutableDictionary — аналог map в C++
NSMutableDictionary * Valutes;
}
// Конструктор принимает на вход URL веб-сервиса
-(RCBDayly ) initWithContentsOfURL:(NSURL)url;
// Для доступа к данным класса будем использовать
// метод getValueForCharCode.
-(NSString ) getValueForCharCode:
(NSString *) char_code;
@end
// А теперь — реализация
@implementation RCBDayly
-(RCBDayly
) initWithContentsOfURL:(NSURL) url
{
// Проинициализируем базовый класс — NSObject
[super init];
// Подгружаем xml,ку с указанного URL
NSError * err = nil;
NSXMLDocument * cbr_xml =
[[NSXMLDocument alloc] initWithContentsOfURL:url
options:0 error:&err];
if (err != nil && [err code] != 0)
{
// Не повезло. Возможно, сервис недоступен, или
// XML-парсер не смог разобрать документ.
// Выяснить, что именно произошло, нам поможет
// locolizedDescription из NSError
NSLog(@"Error:%@", [err localizedDescription]);
// Освободим память, выделенную под наш объект
[self release];
return nil;
}
// Создадим NSMutableDictionary
Valutes = [[NSMutableDictionary alloc] init];
// Нам потребуется массив XML-элементов
NSArray * nodes = nil;
// Заполним его элементами «Valute» из XML’ки
nodes = [[cbr_xml rootElement]
elementsForName: @"Valute"];
// Для каждой валюты получим ее CharCode и
// Value (курс)
for (int i = 0; i < [nodes count]; ++i)
{
NSXMLElement * valute =
(NSXMLElement *)[nodes objectAtIndex: i];
NSArray * names = nil;
names = [valute elementsForName: @"CharCode"];
NSArray * values = nil;
values = [valute elementsForName: @"Value"];
// У валюты есть и имя, и курс?
if ([names count] > 0 && [values count] > 0)
{
// Добавим пару CharCode - Value в наш
// NSMutableDictionary
[Valutes setObject:
[(NSXMLElement *)
[values objectAtIndex: 0] stringValue]
forKey:[(NSXMLElement *)
[names objectAtIndex: 0] stringValue]];
}
// Вернем указатель на себя, как любой нормальный
// конструктор
return self;
}
// В этом методе мы просто обеспечиваем
// доступ к private-полю Valutes
-(NSString
) getValueForCharCode:
(NSString*) char_code
{
return [Valutes objectForKey: char_code];
}
@end
int main(int argc, char *argv[])
{
// Создадим пул для временных объектов,
// которые генерит, например, [NSURL
URLWithString]
NSAutoreleasePool * pool =
[[NSAutoreleasePool alloc] init];
// Создадим объект класса RCPDayly и
// инициализируем его нужным URL
RCBDayly * dayly_values =
[[RCBDayly alloc] initWithContentsOfURL:
[NSURL URLWithString:
@"http://www.cbr.ru/scripts/XML_daily.asp"]];
if (dayly_values == nil)
{
// Произошла ошибка при инициализации класса
return -1;
}
// Почем сегодня доллар? :)
NSLog([dayly_values getValueForCharCode:@»USD»]);
[pool release]; // Освободим пул, а вместе с ним
// и все временные объекты.
return 0;
}

 

Cocoa Framework

Cocoa — набор классов и функций Objective-С, предоставляющих доступ к сервисам Mac OS X для пользовательских приложений. Поскольку для взаимодействия с классами Cocoa необходима среда выполнения ObjC, разработка с использованием этого фреймворка возможна только на Objective-C. Для Сocoa существует возможность кроссплатформенной разработки под Linux и Windows благодаря проектам GNUStep и cocotron.

 

Carbon Framework

Carbon — процедурный фреймворк Mac OS X, предназначенный для использования в приложениях на C/C++. Он предоставляет обратную совместимость с более ранними версиями Mac OS (например, Mac OS 9).

В настоящее время возможности Carbon для работы с Mac OS X становятся все скромнее. Так, например, невозможно получить доступ к GUI из приложения Carbon для 64-битного окружения — Apple отдает предпочтение Cocoa.

 

Links

  • developer.apple.com — если есть вопросы, касающиеся разработки под Mac OS
  • www.cimgf.com — неплохой блог о Cocoa и Objective-C
  • www.gnustep.org — сайт проекта GNUStep
  • www.cocotron.org — кроссплатформенная разработка с использованием ObjC, XCode и Cocoa для Windows

1 комментарий

  1. 24.11.2014 at 22:20

    посоветуйте блоги рассказывающие о базовых навыках работы с xcode

Оставить мнение

Check Also

Алмазный фонд «Хакера». Самые крутые материалы по реверсингу и malware за три года

Иногда мы, редакторы и авторы «Хакера», сами читаем «Хакер». Нет, ну то есть мы постоянно …