Development for iOS
2 December 2010

Objective-C для C# разработчиков

Tutorial
«Если единственный язык, который вы знаете — это Java или C#, я не думаю, что вы профессиональный программист — вы как молодой плотник, который пока работал только с одним видом дерева.»
Дядя Боб


Несколько месяцев назад я начал разрабатывать приложения для iPhone. Переключение с платформы .NET и C# на Cocoa и Objective-C проходило не без приключений, но было достаточно интересным и познавательным. Скоро мне предстоит помогать осваивать новую платформу и другим разработчикам нашей компании. Поэтому решил написать серию вводных заметок, которые, надеюсь, сделают этот переход более плавным.


В этой заметке будет приведен небольшой набор фактов об Objective-C с точки зрения C#-разработчика.

  • Objective-C — это объектно ориентированный язык, «честное» расширение языка С (программа, написанная на C, является программой на Objective-C, что не всегда верно, например, для С++).
    Достаточно подробно о языке было написано в недавней статье Objective-C с нуля.
  • для описания объекта создается два файла — заголовочный с расширением .h и файл реализации с расширением .m
  • классы Objective-C являются объектами, грубо говоря, класс Objective-C можно представить как реализацию паттерна фабричный метод с набором статических методов.
  • множественное наследование, так же как и в C#, не поддерживается.
  • NSObject — это «аналог» базового класса System.Object в .NET.
  • когда мы пишем interface в Objective-C, подоразумеваем class C#.
  • а то, что в C# называем interface, в Objective-C зовем protocol.
  • в Objective-C есть два типа методов — методы класса (их объявление начинаем со знака "+") и методы экземпляра (объявление начинается с "-"). Методы класса, как понимаете, — это те же статические методы С#.
  • если мы хотим вызвать метод объекта, мы посылаем ему сообщение об этом (Objective-C — message-oriented язык, в отличии от function-oriented С#).
  • в Objective-C все методы public (точнее говоря, в нем вообще нет разделения методов по уровням доступа).
  • в Objective-C все методы virtual (то есть любой метод может быть переопределен в классе-наследнике).
  • сборщика мусора в Objective-C, к сожалению, нет (при программировании для iPhone). Вместо него используется механизм подсчета ссылок.
  • чтобы получить привычные нам property, в заголовочном файле используем для объявления свойства ключевое слово @property, а в файле реализации с помощью @syntesize генерируем геттер и сеттер.
  • #import — это using
  • self — это указатель this
  • super — это base
  • id — это как object

Посылка сообщений


Вообще, подход посылки сообщений в Objective-C и, как следствие, способ именования методов — это целая философия. На хабре уже был пост о том, что при виде методов Objective-C у людей выпадают глаза . Эта статья рассчитана на людей, пытающихся запихнуть их обратно. Проще всего это сделать, увидев аналогию с чем-то знакомым.
Давайте посмотрим как выглядит вызов метода в Objective-C. В каждом примере будет приведен реальный аналог на C#.
Метод без параметров:
C#:
someString.ToLower();
Objective-C:
[someString ToLower];

С одним параметром:
someString.Equals(anotherString);
[someString isEqualToString:anotherString];

С несколькими параметрами:
someString.EndsWith(anotherString, true, someCulture);
[someString isEndedWithString:anotherString withIgnoreCase:YES andCultureInfo:someCulture];

Вложенные сообщения:
someString.Substring(1).EndsWith(anotherString.Trim()true, CultureInfo.CurrentCulture);
[[someString getSubstringStartedAtIndex:1] isEndedWith:[anotherString Trim] withIgnoreCase:YES andCultureInfo:[CultureInfo getCurrentCulture]];


Как было написано выше, в Objective-C два вида методов — методы экземпляра и методы класса. Посмотрим как они объявляются в C# и Objective-C.
Метод экземпляра:
public int sum(int firstNumber, int secondNumber);
-(int)sumOfFirstNumber:(int)firstNumber andSecondNumber:(int)secondNumber;

Метод класса (или статический метод в C#):
static int Length(string str);
+(int)Length:(NSString*)str;


Немного о конструкторах и деструкторах


Как и в C# мы можем создать объект, используя ключевое слово new.
[someObject new];

Этот метод аналогичен следующей операции
[[someObject alloc] init];

alloc выделяет память под объект, а init инициализирует этот участок памяти некоторыми дефолтными параметрам. В C# при вызове конструктора эти операции логически не разделяются.
Рекомендуется использовать второй подход, так как он более точно показывает механизм создания объекта (а мы ведь должны понимать, что делаем, верно?) и поддерживает возможность использования различных вариантов инициализации (можем переопределять конструктор).
[[someObject alloc] initWithTitle:@"SomeTitle"];

Вот пример объявления нескольких конструкторов класса NSString:
- (id)init;
Возвращает инициализированный объект NSString, который не содержит символов.
- (id)initWithString:(NSString *)aString;
Возвращает инициализированный объект NSString, копируя символы из другого объекта NSString.
- (id)initWithCharacters:(const unichar *)characters length:(NSUInteger)length;
Возвращае инициализированный объект NSString, содержащий указанное число символов из данного массива символов.

Если init — конструктор в Objective-C (или связка alloc+init), то dealloc — деструктор (освобождение памяти). Как и в C# он вызывается автоматически.

И немного о счетчике ссылок


При переходе с C#, управление памятью при разработке на Objective-C — это один из самых важных вопросов. Мы (.NET разработчики) в этом смысле избалованы — привыкли, что в большинстве случаев за нас все сделает garbage collector. Тут такой подход не пройдет, без внимательной работы с памятью приложения, будете регулярно получать «странные» ошибки.
Механизм счетчика ссылок реализует базовый для всех объектов NSObject. Идея следующая: каждый объект содержит счетчик ссылок, если количество ссылок становится равным нулю, вызывается деструктор (dealloc). Никогда не вызывайте dealloc самостоятельно!
Значение счетчика увеличивается на единицу при выделении памяти под объект (вызове метода alloc/new), при создании копии объекта (сообщение copy), при посылке объекту сообщения retain. Чтобы уменьшить значение счетчика на единицу, нужно послать объекту сообщение release.
Схема работы с объектом обычно такая: создали (счетчик ссылок увеличился на единицу), выполнили необходимые действия(все заинтересованные объекты посылают ему сообщения retain, а затем release), послали сообщение release (уменьшили счетчик на единицу), вызывается деструктор.
С сообщением release нужно быть аккуратным и избегать лишних вызовов, так как повторный вызов деструктора объекта уронит программу.
Также есть возможность избавиться от необходимости помнить о том, что нужно послать объекту сообщение release (иногда это обусловлено тем, что объект возвращается методом и мы не знаем его дальнейшую судьбу). Это можно сделать с помощью сообщения autoRelease и объекта AutoreleasePool.
[[[someObject alloc] init] autorelease];

Объект записывается в AutoreleasePool. То есть сообщение release будет послано объекту «когда-нибудь потом», а до тех пор объект будет находиться в памяти. «Когда-нибудь потом» наступает, когда объекту AutoreleasePool посылается сообщение release или drain.

В продолжение темы советую посмотреть недавний доклад Shivani Khanna «Objective C for C# Developers».
Дополнение/исправления приветствуются.
Спасибо, что прочитали статью!

+69
18.5k 195
Comments 106
Top of the day