Главная » iPhone » Переход к Objective-C iPhone

0

Objective-C был написан в начале 80-х годов прошлого века ученым и разработчиком программного обеспечения Брэдом Коксом (Brad Сох). Он разрабатывался как способ введения возможностей языка Smalltalk в программное окружение С. Большая часть библиотек оболочки iPhone написана на Objective-C, но поскольку этот язык проектировался так, чтобы обеспечить совместимость с языком С, то вы вполне свободно можете применять в вашем приложении С и С++. Objeetive-С в основном используется в Mac OS X и GNUStep (бесплатная оболочка OpenStep). Некоторые языки, например Java и С#, многое позаимствовали из языка Objective-C. Оболочка Cocoa интенсивно использует Objective-C на рабочих станциях Мае, что и было перенесено в iPhone.

Если вы раньше разрабатывали программное обеспечение на Mac OS X, то вы уже знакомы с Objective-C, но если же iPhone стал вашей первой платформой Apple, тогда вы, скорее всего, переходите с С или С++. В этом разделе будут освещены некоторые наиболее существенные различия между этими языками. Если у вас есть опыт программирования на С или С++, то этого должно быть вполне достаточно для того, чтобы начать писать код, используя примеры из этой книги в качестве руководства.

Сообщения

Первое, что вы заметите в Objective-C,— это активное использование скобок. В Objective-C методы не вызываются (called) в обычном понимании этого действия, им отправляются сообщения (messages). Аналогично, метод не возвращает (return), а вместо этого отвечает (responds) на сообщение. В отличие от С, где вызовы функций должны быть предопределены, такой стиль Objective-C обмена сообщениями позволяет разработчику динамически создавать новые методы и сообщения прямо во время исполнения. Обратной стороной этого является то, что становится вполне вероятной отправка объекту такого сообщения, на которое он не сможет ответить, что в результате приведет к исключению и, скорее всего, к аварийному завершению программы.

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

returnValue = [ myWidget powerOn ];

Аналог этого действия на С++ может выглядеть так:

returnValue = myWidget->powerOn();

Аналог на С может объявить функцию внутри его плоской системы имен:

returnValue = widget_powerOn(myWidget);

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

Приведенный далее пример вызывает метод setspeed и передает два аргумента:

returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ];

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

returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ]; returnValue = [ myWidget setSpeed: 10.0 withGyroscope^: 10.0 ];

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

Несмотря на то, что классы С++ мсЗгут быть определены в Objective-C, основной причиной применения данного языка является возможность использования собственных объектов и средств Objective-C. Это относится и к использованию им интерфейсов. В стандартном С++ классы являются структурами, а их переменные и методы заключены внутри этих структур. С другой стороны, Objective-C хранит свои переменные в одной части класса, а методы — в другой. Этот язык также требует того, чтобы объявление интерфейсов осуществлялось специальным образом в собственном блоке кода (под названием @interface) отдельно от блока, содержащего реализации (под названием @ implementation). Сами методы также построены в манере Smalltalk и очень слабо походят на обычные функции С.

Интерфейс для нашего простенького примера может выглядеть так, как показано в листинге 2.1, являющемся файлом MyWidget.h.

#import <Foundation/Foundation.h>

@interface MyWidget : BaseWidget {

BOOL isPoweredOn; @private float speed; @protected float mass; Sprotected float gyroscope;

+ (id)alloc; + (BOOL)needsBatteries;

-   (BOOL)powe rOn;

-   (void)setSpeed:(float)_speed;

-   (void)setSpeed:(float)_speed withMass:(float)jnass;

-   (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope; @end

Все важные семантические элементы в этом файле будут объяснены в следующих разделах.

Импорт

Директива препроцессора #import замещает обычную директиву # include (хотя #include все равно может использоваться). Единственное преимущество использования #import состоит в том, что она является встроенной логикой, гарантирующей, что один и тот же ресурс никогда не будет включен более одного раза. Такой подход замещает обходной способ использования макро-флагов, обычно применяемый в коде С: #ifndef _MYWIDGET_H #define _MYWIDGET_H

#endif

Объявление интерфейсов

Интерфейс объявляется оператором @interface с последующим указанием имени интерфейса и базового класса (если таковой есть), от которого он был порожден. Блок заканчивается оператором @end.

Методы

Методы объявляются вне структур в фигурных скобках. Знак плюса (+) определяет метод как статический, а знак минуса (-) объявляет экземпляр метода. Таким образом, метод alloc (для назначения нового объекта) будет вызываться с помощью ссылки напрямую в класс MyWidget, в то время как методы, являющиеся специфическими для экземпляра класса MyWidget, например, needBattaries и powerOn, будут вызваны на экземпляр, возвращаемый alloc.

Каждый объявленный аргумент для метода представлен типом данных, локальными именами переменных и необязательно внешними именами переменных. Примерами внешних имен переменных в листинге 2.1 являются withMass и withGyroscope. Вызываемая функция (notifier), которая вызывает метод, ссылается на внешние имена переменных, но внутри метода на аргументы ссылаются, используя их локальные имена переменных. Таким образом, метод setspeed использует переменную local mass для получения значения, передаваемого как withMass.

Если в объявлении нет внешнего имени переменной, то на переменную ссылаются только с двоеточием, например, : ю. о.

Реализация

Суффиксом кода для исходных файлов Objective-C является .т. Реализация скелета простейшего класса из последнего раздела может быть такой, как показано в листинге 2.2 (файл MyWidget.m).

#import "MyWidget.h" @implementation MyWidget

+ (id)alloc {

}

+ (BOOL)needsBatteries { return YES;

}

- (BOOL)powerOn {

isPoweredOn = YES; return YES;

-   (void)setSpeed:(float)_speed {

speed = speed;

}

-   (void) setSpeed: (fl’oat)_speed withMass: (float)_mass {

speed = _speed; mass = _mass;

}

-   (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope {

speed = _speed; gyroscope = _gyroscope;

}

@end

Так как интерфейс заключен в собственный блок кода, то реализация начинается с оператора @implementation и заканчивается оператором @end. Эта весьма распространенная практика в С++ использовать для переменных экземпляра в качестве префикса т_, чтобы публичные методы могли принимать имена переменных. Это облегчает повторное использование чужого кода, поскольку становится возможным определить назначение переменной по ее имени. Поскольку Objective-C разрешает использование внешних имен переменных, то метод может предоставлять разработчику для использования осмысленное имя. в то время как внутри использовать собственное имя. Это истинное имя затем может быть использовано внутри объекта, а локальное имя переменной метода имеет в качестве префикса символ подчеркивания, например, speed.

Категории

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

классам, не затрагивая другие объекты.

к

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

Различие между категориями и наследованием такое же, как между тюнингом вашей машины и украшением ее для использования в качестве праздничной платформы на параде. Когда вы модернизируете вашу спортивную машину, то добавляете внутрь автомобиля новые компоненты, в результате чего он начинает иначе функционировать. Иногда какие-либо компоненты даже полностью вытаскиваются и заменяются на новые. Факт добавления нового компонента в двигатель, например, турбо, влияет на функционирование всего автомобиля. Это принцип работы наследования.

С другой стороны, категории больше походят на праздничную платформу, в которой автомобиль остается абсолютно нетронутым, а макеты из картона, папье-маше прикрепляются к автомобилю с внешней стороны, делая его неузнаваемым. В контексте парада автомобиль выступает в роли совершенно иного "животного", но для механика он все тот же ваш старый автомобиль, которым вы управляете много лет.

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

selfDestruct. Кроме того, данная категория замещает метод powerOn собственным. Сравните применение здесь круглых скобок для MySpaceWidget, содержащего класс, с использованием двоеточия в листинге 2.1 для обеспечения наследования:

#import "MyWidget.h"

Sinterface MyWidget (MySpaceWidget)

-   (void)selfDestruct;

-   (BOOL)powe rOn; @end

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

#import "MySpaceWidget.h" @implementation MyWidget (MySpaceWidget)

-   (void)selfDestruct {

isPoweredOn = 0; speed = 1000.0; mass = 0;

}

-   (BOOL)powerOn {

if (speed = 0) {

isPoweredOn = YES; return YES;

‘}

/* He включать питание, если космический корабль движется. */ return NO;

} »..’ • @end

Маскировка

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

: implementation MyAutonomousWidget

В предыдущих примерах были созданы классы виджетов (widgets). Теперь после создания этих виджетов в некоторый момент была открыта вечная энергия. Это позволило многим новым виджетам стать автономными, в то время как некоторые устаревшие виджеты продолжают работать на аккумуляторах. Поскольку автономные виджеты имеют существенное количество совершенно другого кода, то был порожден новый объект MyAutonomousWidget, чтобы подменить всю измененную функциональность, например, статический метод needBatteries (см. листинги 2.4 и 2.5).

#import <Foundation/Foundation.h> #import "MyWidget.h"

@interface MyAutonomousWidget : MyWidget

}

- (BOQL)needsBatteries; (?end

= import "MyAutonomousWidget.h"

+ (BOOL)needsBatteries { return NO;

} > • @end

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

MyAutonomousWidget *myAutoWidget = [ MyAutonomousWidget alloc ]; MyWidget *myWidget = [ MyWidget alloc ]; class_poseAs(myAutoWidget, myWidget);

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

Для дальнейшего изучения

Чтобы узнать больше о программировании на Objective-C, обратитесь к приведенным далее великолепным ресурсам от O’Reilly:

?         James Duncan Davidson, "Learning Cocoa with Objective-C", Second Edition http://www.oreilly.com/catalog/learncocoa2/;

?         Andrew M. Duncan, "Objective-C Pocket Reference" http://www.oreilly.coni/catalog/objectcpr/.

Источник: Здзиарски Дж. iPhone. Разработка приложений с открытым кодом: Пер„с англ. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2009. — 368 е.: ил.

По теме:

  • Комментарии