]]> ]]>
Править | Обсудить | История

Objective-C

Дата создания:
1986
Создан под влиянием:
Повлиял на:
Парадигма:
Типизация:
Принятые расширения файлов:
.m, .l
Реализации и версии (свернуть все | развернуть все):
Язык программирования

Язык является надмножеством языка C (любой C код компилируется Objective-C компилятором). В язык добавлены: поддержка объектно-ориентированного программирования в стиле Smalltalk (посылка сообщения вместо вызова метода), строки, логический тип (BOOL, значения YES или NO), и др.

Язык отличается от C++ большей гибкостью и количеством метаинформации. При каждом вызове метода Objective-C runtime просматривает таблицу виртуальных методов в поисках нужного селектора. Если метод не найден, вызывается универсальный обработчик. У обычных объектов этот обработчик сигнализирует об ошибке (например, выбросом исключения). А у прокси–объектов этот обработчик конструирует удалённый вызов. Сделать прокси в Objective C относительно просто. Метаинформация об объектах содержит сведения о методах и типах аргументов. Достаточно сведений, чтобы прокси объект мог корректно интерпретировать содержимое стека и сериализовать вызов.

На низком уровне вызов метода выглядит как вызов objc_sendmsg(object, selector, arguments). Селектор — это строка, состоящая из идентификаторов, разделённых двоеточиями. Например, если вызов имеет вид:

[mycalendar ordinalityOfUnit:myunit inUnit:myunit2 forDate:mydate]

…то селектор этого вызова “ordinalityOfUnit:inUnit:forDate:”. Непосредственно в objc_sendmsg передаётся не строка, а 32-битное число. Каждая динамическая библиотека и сама программа при инициализации регистрируют все используемые селекторы, устанавливая соответствие между строками и числами.

arguments — это varargs языка C.

Такой способ вызова позволяет частично бороться с проблемой fragile base class. С одной стороны, API системы подлинно объектно–ориентированное. Например, любая Cocoa-программа содержит наследников NSController, который реализован в системной инфраструктуре. С другой стороны, можно относительно легко добавлять новые API к уже существующим классам, являющимися базовыми для пользовательских программ. Именно так Apple и вводит новые API. Пользовательские программы, будь то Camino или Roxio Toast Titanium, не требуют при этом никакой перекомпиляции. Неполнота этого подхода заключается в том, что можно добавлять новые методы, но нельзя добавлять новые поля. Чтобы бороться с этим ограничением, либо с самого начала резервируется достаточно места на будущее, либо создаётся поле типа id, содержащее указатель на внутреннюю реализацию.

По аналогии с интерфейсами в Java, в Objective C есть протоколы. Протокол определяет набор селекторов, которые должен поддерживать объект, реализующий протокол.

В языке определён специальный тип id. id отличается от void только тем, что компилятор позволяет вызывать методы id, но не позволяет делать это для void. Во время выполнения любой указатель на объект не отличим от id. Вся разница во время компиляции. Во время компиляции компилятор проверяет поддержку селектора у класса объекта. Например, если переменная имеет тип NSController*, компилятор сделает предупреждение при попытке вызвать неизвестный селектор. Предупреждение сделает, но скомпилировать всё же позволит. Это может быть полезно, если объект являет потомком NSController или вообще не является NSController, что вполне возможно. Поскольку работа со всеми объектами происходит через указатели, а приведение типов для объектов влияет только на восприятие объекта компилятором (никаких runtime проверок), становится возможным использовать утиную типизацию. Используя функции библиотеки Objective C (начинаются на objc_), в принципе, можно выяснить подлинную природу объекта, но компилятор автоматически такие проверки не создаёт.

Для создания объекта в Objective-C необходимо вызвать метод класса (похожая схема используется в Ruby). Это делает использование таких паттернов, как абстрактная фабрика или синглтон (одиночка), тривиальными.

Другие особенности:

Для строковых литералов используется запись вида @”строка”. Это создаёт строку типа NSString, а не char*, как в C.

Вместо директивы #include для заголовочных файлов Objective-C рекомендуется использовать директиву #import, которая гарантированно подключает заголовочный файл только один раз.

Несколько изменена схема поиска файла заголовка с учётом каркасов (framework). Каркас — это папка, имеющая расширение .framework. Внутри каркаса могут подпапки (или символические ссылки на них) Headers, Resources, Frameworks. Препроцессор при обработке #import <Cocoa/Cocoa.h> находит файл Cocoa.framework/Headers/Cocoa.h Препроцессор без поддержки каркасов не сможет найти этот файл.

Элементы синтаксиса:

Комментарий до конца строки //
Комментарии, которые могут быть вложенными #if 0 ... #endif
Комментарии, которые не могут быть вложенными /* ... */
Регистрозависимость Yes
Регулярное выражение идентификатора переменной [_a-zA-Z][_a-zA-Z0-9]*
Присваивание значения переменной variable = value
Объявление переменной type variable
Объявление переменной с присваиванием значения type variable = value
Блок { ... }
Равенство ==
Неравенство !=
Сравнение < > <= >=
Определение функции returntype f(type1 p1, type2 p2, ...)
Вызов функции f(a, b, ...)
Вызов функции без параметров f()
Последовательность ;
Если - то if (condition) ...
Если - то - иначе if (condition) ... else ... OR ( condition ? ... : ... )
Бесконечный цикл while (1) ...
Цикл с предусловием while (condition) ...
Цикл с постусловием do ...while (!condition)
Цикл for - next для диапазона целых чисел с инкрементом на 1 for (int i = 1; i <= 10; i++) ...
Цикл for - next для диапазона целых чисел с декрементом на 1 for (int i = 10; i >= 1; i--) ...

IDE/Редакторы:

Примеры:

Факториал:

Пример для версий GCC 3, GCC 4, TCC 0.9.25, gcc 3.4.5, gcc 3.4.5 (Objective-C)

Используется рекурсивное определение факториала.

#include <stdio.h>

unsigned long long factorial(unsigned long long n)
{
    if (n == 0) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

int main(void)
{
    int n;
    for (n = 0; n <= 16; n++) {
        printf("%i! = %lld\n", n, factorial(n));
    }

    return 0;
}

Квадратное уравнение:

Пример для версий Borland C++ Builder 6, Microsoft Visual C++ 9 (2008), TCC 0.9.25, g++ 3.4.5, gcc 3.4.5, gcc 3.4.5 (Objective-C)

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

#include <math.h> 
#include <stdio.h>

int main()
{
  int A, B, C, D;
  printf("A = ");
  scanf("%d", &A);
  if (A == 0) {
    printf("Not a quadratic equation.\n");
    return 0;
  }
  
  printf("B = ");
  scanf("%d", &B);
  printf("C = ");
  scanf("%d", &C);

  D = B * B - 4 * A * C;
  if (D == 0) {
    printf("x = %f\n", -B / 2.0 / A);
    return 0;
  }
  
  if (D > 0) {
    printf("x1 = %f\nx2 = %f\n",
           (-B + sqrt(D)) / 2.0 / A, (-B - sqrt(D))/ 2.0 / A);
  } else {
    printf("x1 = (%f, %f)\nx2 = (%f, %f)\n",
           -B / 2.0 / A, sqrt(-D) / 2.0 / A, -B / 2.0 / A, -sqrt(-D) / 2.0 /A);
  }
  return 0;
}

Числа Фибоначчи:

Пример для версий TCC 0.9.25, gcc 3.4.5, gcc 3.4.5 (Objective-C)

Используется рекурсивное определение чисел Фибоначчи.

#include <stdio.h>

int fibonacci(int n)
{
    return ( n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) );
}

int main(void)
{
    int n;
    for (n=1; n<=16; n++)
        printf("%d, ", fibonacci(n));
    printf("...\n");
    return 0;
}

CamelCase:

Пример для версий TCC 0.9.25, gcc 3.4.5, gcc 3.4.5 (Objective-C)

Пример основан на посимвольной обработке строки. Функция gets читает строку до конца строки. Следует отметить, что эта функция считается “опасной” из-за отсутствия контроля того, сколько символов введено, и возможных ошибках доступа к памяти. В C нет логического типа данных, поэтому его приходится симулировать целой переменной.

#include <stdio.h>

void main() {
    char text[100],cc[100];
    gets(text);
    int i,j=0,lastSpace=1;
    for (i=0; text[i]!='\0'; i++) 
        if (text[i]>='A' && text[i]<='Z' || text[i]>='a' && text[i]<='z')
        {   if (lastSpace>0)
                cc[j] = toupper(text[i]);
            else
                cc[j] = tolower(text[i]);
            j++;
            lastSpace = 0;
        }
        else
            lastSpace = 1;
    cc[j]='\0';
    printf("%s\n",cc);
}

Hello, World!:

Пример для версий Borland C++ Builder 6, TCC 0.9.25, g++ 3.4.5, gcc 3.4.5, gcc 3.4.5 (Objective-C)
#include <stdio.h>

int main()
{
    printf("Hello, World!\n");
    return 0;
}

CamelCase:

Пример для версий gcc 3.4.5 (Objective-C)
#import <Foundation/Foundation.h>

#define LETTERS @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

NSString *camelCase(NSString *s) {
  return [[[s capitalizedString] componentsSeparatedByCharactersInSet:[[NSCharacterSet characterSetWithCharactersInString:LETTERS] invertedSet]] componentsJoinedByString:@""];
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"%@", camelCase(@"Test Example One"));
    NSLog(@"%@", camelCase(@"exampleTwo "));
    NSLog(@"%@", camelCase(@"!!! is_this_3RD EXAMPLE?.."));

    [pool drain];
    return 0;
}

Комментарии

]]>

blog comments powered by Disqus

]]>

Работа программистам