Objective-C для Java-программистов
Как все начиналось
Как как. А как обычно. В один прекрасный момент у нас завелся замечательный зверь, под названием iPod-Touch.

Наш iPod. Игрушка для программиста
Так как в нашей “семье” программистов ни одно такое приобретение просто так не проходит, то меня “пересадили” с Flash’а, на iPod. А вообще я на Java-е программирую. Впрочем это не важно. Думаю, что это не последний язык, с которым мне пришлось столкнуться.
Я описал некоторые сложности и особенности, которые, считаю, необходимо знать, при переходе с Java на Objective-C для разработки приложений на iPhone(iPod). Я рассмотрел несколько вопросов, таких как, откуда взялся этот Objective-C и с чем его едят, особенности объектно ориентированного подхода в нем, как тут называются вещи, к котором так привыкли Java-программисты (вызовы методов, интерфейсы, и т.п.), на которые следует обратить внимание, чтобы не наступить на те же грабли, что когда-то наступил и я (грабли очень устали, и просили их больше по возможности не тревожить).
Objective-C
Прочитать про него можно вот на википедии. А мысли по поводу того, насколько он Objective, я буду выкладывать тут. Я сначала подумал “Слава тебе, ‘ТОТ, КТО ГДЕ-ТО ЕСТЬ’”, объектно-ориентированный С! Забываем про вызовы каких-то левых функций непонятно откуда, про множественное наследование, и про другие фичи, которые мне не нравились в С, здесь все будет нормально. Не тут-то было! Первым на меня набросилось
@class definition
Во-первых, каждый класс лежит в двух файлах, в одном, обычно имеющем расширение “.h” описывается интерфейс(@interface) класса (просьба не путать с Java-интерфейсами), в другом, имеющем расширение “.m “, описывается реализация( @implementation) класса. В принципе, это не так критично, можно достаточно быстро привыкнуть. Выглядит это примерно следующим образом
[sourcecode language='c']
//MyClass.h
@interface MyClass : SuperClass {
//определение переменных экземпляра
}
//определение методов
@end
....
//MyClass.m
#include
@implementation MyClass
// реализация методов
// реализация методов протокола
[/sourcecode]
Теперь пару слов об интерфейсах, которые описываются в файлах-заголовках. Это совсем не те интерфейсы, что есть в Java-е. Если проводить аналогии, то интерфейсы в Objective-C это абстрактные классы в Java-e. А вот аналогом джавовских интерфейсов в обжектив-цы есть протоколы(@protocol), причем они могут иметь опциональные методы, то есть те, которые не обязательно реализовывать в конечном классе. После джавы очень непривычно, но “маємо, те що маємо”(с). Итак теперь пару слов о такой вещи как
@protocol
Как я уже только что говорил, протоколы — это прямая аналогия интерфейсам в джаве. То есть, они просто содержат описание методов, которые должны быть реализованы в классе, имплементирующем протокол(интерфейс). Из особенностей можно отметить необязательные @optional методы, которые могут быть описаны в протоколе(интерфейсе). Вот синтаксис их описания
[sourcecode language='c'] // описывается в любом .h файле @protocol MyProtocol // определение методов @optional // определение необязательных методов // @end // А вот и пример @protocol MyProtocol - (id) doRelax: (NSString *) relaxType; // вот это очень удобно @optional - (void) doWork: (NSString *) workType; @end [/sourcecode]
Где-то так. Самое интересное, что компилятор не особо ругается, если не все методы интерфейса реализованы. Просто предупредит. Ошибка вылезет на этапе выполнения, и то, если будет вызван не реализованный метод. С этим помогает бороться @optional директива — и компилятор спокоен, и ошибок не будет. Дальше я не буду особо вдаваться в синтаксис, ибо это очень хорошо описано на википедии. Особенности, особенности, и еще раз особенности. Еще одной очень полезной вкусностью в Objective-C есть
@property и @synthesize
Очень часто в Java-проектах можно найти файлы-контейнеры, которые просто содержат информацию, и не имеют никаких методов, кроме методов инициализации и получения/изменения данных — то есть ровно столько же сеттеров и геттеров, сколько и переменных в классе. Обычно описываются переменные, а потом автогенерируются все сеттеры и геттеры. Класс становится страшно большим, и самое обидное, что никто не смотрит на все эти гет-сет методы. Зачем, если можно просто посмотреть на переменную, и понять, что в 999 из 1000 случаев, в классе есть такой геттер/сеттер. В общем, директива компилятора @property имеет под собой функцию описания доступа к переменным класса. «Тебе страшно? Мне — нет.»(с) Карлсон.
На примере станет понятнее.
[sourcecode language='c']
//Restaurant.h
@interface Restaurant : NSObject {
NSObject *name;
NSString *url;
NSObject *zipPrivate;
}
@property(nonatomic,readonly) NSObject* name;
@property(nonatomic,retain, getter=getTheURL) NSString *url;
@property(nonatomic,retain) NSObject *zip;
@end
//Restaurant.m
#import "Restaurant.h"
@implementation Restaurant
@synthesize name;
@synthesize url;
@synthesize zip = zipPrivate;
- (void)dealloc {
[url release];
[zip release];
[super dealloc];
}
@end
[/sourcecode]
Вот и все. Как видим, тут тоже не обошлось без предварительного объявления. В результате получаем класс, с набором сеттеров и геттеров, плюс в добавок ко всему получаем возможность доступаться к свойствам класса через точку
[sourcecode language='c'] restaurant.name = @"myRest"; [restaurant setName:@"myRest"]; [/sourcecode]
а теперь о том, как можно параметризировать генерацию свойств класса.
- nonatomic — параметр, чем-то похожий на synchronized в Java, вернее на его отсутствие. Если мы не пишем многопоточные приложения , то ставим nonatomic — не ошибемся
- readonly — вроде должно быть понятно по смыслу. Если нет — то это параметр, говорящий о том, что свойство класса только для чтения. Сеттер не генерируется, при попытке присвоить что-нибудь свойству класса возникает ошибка на этапе компиляции.
- retain - ой.. как бы так сказать… Указывать надо, если мы хотим, чтоб значение сохранялось, вне зависимости от того, что творится в системе. Параметр говорит о том, что присваивание будет происходить по ссылке + мы захватываем ссылку, и не даем garbage collectorу, в случае чего, освободить память по этой ссылке. Не забываем «отпускать» такие свойства в методе «dealloc».
- сopy — присваивание происходит путем копирования данных. Объект, который присваивается должен реализовывать протокол NSCopying. В джаве, это выглядело бы как b = a.clone();
- assign(по умолчанию) – простое присваивание по значению. Отличие от retain в том, что в этом случае , garbage collector может спокойно освободить память, на которую эта ссылка указывает (если это ссылка, конечно;).
Пример, где может вылезти проблема, при неправильном указании метода доступа, я еще покажу.
А теперь чуть-чуть про @synthesize. Это директива компилятора, которая где-то за кулисами вставляет геттер и сеттер. Обычно имя свойства и имя переменной совпадают, но, иногда, приходится делать так, чтобы свойство работало с переменной с другим именем, в примере таким свойством есть zip, которое работает с пременной zipPrivate.
Вот такая вот печенька. Очень удобно. Хотелось бы, чтобы это в каком-нибудь виде появилось в Java. Было бы замечательно. Но, пока это все — мечты. А теперь, о несильно приятных вещах для Java-программистов.
Освобождение памяти, да и просто работа с памятью
Да. Память нужно освобождать. Забудьте о том, что кто-то это будет делать за вас. Вы, вы и только вы ответственны за ее освобождение. Даешь свободу памяти собственными руками! С другой стороны, нужно следить за тем, чтобы память, которую вы используете, не освободил кто-нибудь другой, ну или вы не освободили память, которую кто-нибудь использует
Итак, выделение памяти под объект осуществляется методом alloc, освобождение - dealloc. Метод dealloc САМИ РУКАМИ НЕ ВЫЗЫВАЕМ, вместо этого вызываем метод release.
Это работает следующим образом: у каждого объекта есть счетчик ссылок, который устанавливается в 1 при создании(alloc). Каждый метод retain увеличивает этот счетчик на 1, каждый метод release — уменьшает на 1. Если счетчик равен нулю — то память освобождается (насколько я заметил, освобождается сразу же).
А теперь, главное правило, которое сильно поможет — вы отвечаете за освобождение памяти только тех объектов, которые создали сами, либо у которых вы вызвали retain метод. Все остальные объекты — это не ваша забота. То есть, получается, что каждому alloc или retain, должен соответствовать свой release. Кроме этого, если объект создавался через каку-нибудь фабрику, или функцию, ctx = CGContextCreate() (именно создавался, а не брался откуда-то готовый), то надо бы найти соответсвующую функцию осовобождения объекта CGContextRelease(ctx).
А теперь — несколько примеров (один;) по работе с памятью.
[sourcecode language='c'] // Все те же рестораны Restaurant * b = [Restaurant alloc]; //создаем ресторан NSObject * obj = [NSObject alloc]; // создаем объект (кол-во ссылок на объект КСО= 1) b.url = obj; // присваиваем свойству, КСО = 1, т.к. метод доступа - "assign" [obj release]; // осовобждаем объект, КСО = 0, память освобождается b.zip = b.url; // Вылетаем, так как b.name указывает туда, где уже ничего нету //А вот тот же случай, но для свойства "zip", у которого метод доступа - "retain". Restaurant * b = [Restaurant alloc]; //создаем ресторан NSObject * obj = [NSObject alloc]; // создаем объект (кол-во ссылок на объект КСО= 1) b.zip = obj; // присваиваем свойству, КСО = 2, т.к. метод доступа - "retain" [obj release]; // осовобждаем объект, КСО = 1 b.url= b.zip; // Все нормально, мы все еще можем доступиться до объекта через b.zip [/sourcecode]
Вообще, по работе с памятью нужна отдельная статья, впрочем, достаточно хорошо все описано в документации
@rake

Нет, это не директива компилятора ;) Это – @грабли. Да, те самые, на которые я несколько раз наступил, пока изучал objective-C. Я рассмотрел лишь часть особенностей, как мне кажется, необходимых, для того, чтобы нормально стартовать. Думаю, что кому-то эта статья поможет пару раз от них увернуться.






Павел Башмаков
в 14:48, 23.01.2009Я хочу статью про программирование на iPod :) Чтобы и моей бабушке было понятно.
Бубнов Славик
в 9:19, 26.01.2009Прикольно написано: доходчиво и с юмором )))
Но надо пробовать, а не только читать ;)
Роман Мазур
в 20:55, 27.01.2009Оригінальний синтаксис :). Ці квадратні дужки, мінуси, як списки…
Павел Башмаков
в 21:59, 27.01.2009Ага, как списки в Прологе.
Ребята сами себе коментим. Ой нехорошо. Люди скажут, что псевдоактивность создаем :)
NoOne
в 13:53, 28.01.2009Я как-раз Java-разработчик и как-раз знакомлюсь с Objective-C (хочу попробовать подевелопить под iPhone, пока-что в сугубо личных энтузазистских интересах :)). Так что статья актуальной оказалась (да и написана внятно), спасибо (продолжайте в том же духе! ;)).
White
в 13:43, 29.01.2009Не упомянут autorelease, а также принятый в Obj-C метод init (с разновидностями), который вызывается после alloc (Class *obj = [[Class alloc] init]) и инициализирует свойства.
Кроме того, всё-таки не стоит смешивать авторелиз-пул со сборщиком мусора. В последней версии Obj-C есть сборщик мусора, но он не реализован для платформы iPhone, ибо нефиг загружать ресурсы бедного телефона. :)
PS Проектов на iPhone ещё не планируете?
Kilew
в 16:05, 29.01.2009Уже сделали ;) (проект;)
А по поводу памяти + еще авторелиз. Говорю, ж нужно отдельно писать ;)
В ближайшее время более подробно, все-таки остановлюсь на работе с памятью.
Павел Башмаков
в 18:39, 01.02.2009Меня держит пару моментов, которые мы недоделали в нашем первом iPhone приложении от написания поста на эту тему. То карту вставить другую, то где-то доп. проверку добавить iPhone или iPod, то оригинальный вид рассписания работы заведения американская душа не признает :) Вобщем всякие мелочи, но которые, в то же время, очень важные и сильно влияют на user experience. На следующей недели такая статья уже точно должна появиться.
Novacoder
в 17:01, 13.04.2009Помоему есть небольшая ошибочка в коде примера :)
Если в примере с использованием памяти используется класс ресторана, описанный немного выше, то b.name = obj не сработает, т.к. property name – readonly.
Но думаю, что все тут поняли, что имелось ввиду, что свойство name – только assigned (by default) и not readonly.
Но исправить в статье стоит.
Павел Тайкало
в 9:58, 13.05.2009Поправил, спасибо ;)
New
в 21:56, 12.02.2010Большое спасибо за статью!! (Хотя уже и сколько времени прошло с ее написания). Перешла с Java на Obj C с некоторыми муками для себя, жаль раньше не наткнулась. В принципе грабли у всех похожи,я так поняла)
Разработка iPhone/iPod touch приложений: Начало работы с Interface Builder < Stanfy Блог
в 9:29, 13.05.2009[...] с Objective-C работаем с объектами.. и памятью или Objective-C для Java-программистов. Удачи в изучении!!! Таги: iPhone, iPhone & iPod, [...]
kasblog » Blog Archive » Ссылки по Objective-C
в 16:34, 29.10.2009[...] http://stanfy.com.ua/blog/2009/01/22/objective-c-for-java-programmers/ [...]
Проект crazymenu – опыт интернет интеграций < Stanfy Блог
в 15:08, 12.12.2011[...] опыте изучение это платформы вы можете прочитать здесь. Итак, скоро: ищите нашу первую iPhone разработку на [...]