Objective с руководство по

Время на прочтение
23 мин

Количество просмотров 429K

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

Банальная теория возникновения ООП

Проблема повторного использования написанного кода и его переносимость постоянно заставляет программистов искать все новые пути его упорядочивания, структуризации и абстрагирования. Для решения этих проблем создаются новые парадигмы программирования, шаблоны проектирования, новые языки, компиляторы и стандартные библиотеки к ним, программные платформы и фреймворки. Так образовались парадигма подпрограмм (процедур), реализуемая при помощи процессорных команд CALL\RET и стека (по сути, перенос потока выполнения по адресу произвольной, а не следующей за текущей команды, с последующим возвратом). Затем, парадигма модулей (каждый файл – отдельная единица трансляции), породившая двухэтапную трансляцию: компиляция модулей, а затем их компоновка (статическая или динамическая) в исполняемый модуль.

В следствии увеличения объема кода в проектах и сложностей его поддержки, с 1960х начинает образовываться новая, объектно-ориентированная парадигма программирования, разбившая программы на еще более мелкие составляющие – типы данных. Ее суть заключается во взаимодействии сущностей (объектов) посредством посылки друг другу сообщений. Каждый объект является переменной определенного программистом типа данных (так называемого класса). Определение такого специального пользовательского типа данных (класса) заключается в двух вещах: определении набора данных (инвариантов, членов) и набора подпрограмм (методов), которые будут их обслуживать.

Класс обычно оформляется как определенный программистом тип, основанный на встроенных (языковых) типах данных и\или других классах. Для языка С, не поддерживающего объектно-ориентированную парадигму, это может быть структура (struct). А набор подпрограмм реализуется как обычные функции, обязательно принимающие как минимум один параметр — указатель на набор данных, подлежащих обработке.

Основным преимуществом объектно-ориентированного подхода стала возможность создавать новые классы на основе уже написанных (добавлять инварианты и методы, переопределять методы, использовать определенные в базовом классе методы как свои), названное наследованием.

Набор методов представляет собой интерфейс для взаимодействия с инвариантами. Невозможность непосредственной модификации данных класса (без задействования его интерфейса) отражает принцип инкапсуляции. На рисунке показан класс и его объекты. Имеется инвариант x типа float и к нему интерфейс (метод) doubleX, возвращающий значение инварианта.

Бывает, что необходимо послать сообщение объекту, который на него определенно отвечает (т.е. вызвать для объекта класса такой метод, который он реализовал), но, по ситуации, конкретный класс этого объекта неизвестен. Например, каждому элементу списка указателей на объекты класса Auto нужно послать сообщение Move, а известно что в списке находятся указатели на объекты не только класса Auto, но также и указатели на производные (наследованные) классы Ford и Subaru. Это возможно сделать только благодаря принципу полиморфизма, заключающегося в том, что при посылке определенного сообщения объекту из некой иерархии классов, в которой все объекты способны принять такое сообщение, этот объект реагирует на него соответственно своему, а не базовому для данной иерархии классу.

Первым языком с поддержкой объектно-ориентированного подхода стал Simula67. Затем появился Smalltalk. А в 80х начал оформляться С++ — основной язык современного системного программирования. Его расширение и усовершенствование в 90х породило ряд парадигм и шаблонов проектирования, и оказало необратимое влияние на современное видение объектно-ориентированного подхода, в том числе, и на язык Objective-C.

Чуть-чуть истории

Objective-C возник в 80-x как модификация С в сторону Smalltalk. Причем модификация эта состояла в добавлении новых синтаксических конструкций и специальном препроцессоре для них (который, проходя по коду преобразовывал их в обычные вызовы функций С), а также библиотеке времени выполнения (эти вызовы обрабатывающей). Таким образом, изначально Objective-C воспринимался как надстройка над C. В каком-то смысле это так и до сих пор: можно написать программу на чистом С, а после добавить к ней немного конструкций из Objective-C (при необходимости), или же наоборот, свободно пользоваться С в программах на Objective-C. Кроме того, все это касается и программ на С++. В 1988 NeXT (а в последствии Apple) лицензировала Objective-C и написала для него компилятор и стандартную библиотеку (по сути SDK). В 1992 к усовершенствованию языка и компилятора подключились разработчики проекта GNU в рамках проекта OpenStep. С тех пор GCC поддерживает Objective-C. После покупки NeXT, Apple взяля их SDK (компилятор, библиотеки, IDE) за основу для своих дальнейших разработок. IDE для кода назвали Xcode, а для GUI – Interface Builder. Фреймворк Cocoa для GUI разработок (и не только) на сегодня является наиболее значимой средой разработки программ на Objective-C.

Особенности Objective-C

Файлы модулей на Objective-C имеют расширение “.m” (если использовалась смесь С++ и Objective-С, то расширение “.mm”). Заголовочные файлы – “.h”. Все, создаваемые в Objective-С объекты классов должны размещатся в динамической памяти. Поэтому особое значение приобретает тип id, который является указателем на объект любого класса (по сути void *). Нулевой указатель именуется константой nil. Таким образом, указатель на любой класс можно привести к типу id. Возникает проблема: как узнать к какому классу относится объект, скрывающийся под id? Это делается благодаря инварианту isa, который присутствует в любом объекте класса, унаследовавшего специальный базовый клас NSObject (приставка NS обозначает NeXT Step). Инвариант isa относится к зарезервированному типу Class. Объект такого типа позволяет узнавать имена своего и базового класса, набор инвариантов класса, а также прототипы всех методов, которые реализовал этот объект и их адреса (посредством локального списка селекторов). Все зарезервированные слова Objective-C, отличающиеся от зарезервированных слов языка С, начинаются с символа @ (например @protocol, selector, interface). Обычно имена инвариантов классов с ограниченной областью видимости (@private, protected) начинаются с символа подчеркивания. Для строк в Cocoa имеется очень удобный класс NSString. Строковая константа такого класса записывается как @”Hello world”, а не как обычная для С строковая константа “Hello world”. Тип BOOL (по сути unsigned char) может принимать константные значения YES и NO. Все особые для Objective-C зарезервированные слова (которые отличаются от языка С и находятся в заголовочном файле objc/objc.h) приведены ниже:

  • interface Начинает объявление класса или категории (категория – расширение класса дополнительными методами без наследования)
  • @implementation Начинает определение класса или категории
  • @protocol Начинает объявление протокола (аналог класса С++, состоящего из чисто виртуальных функций)
  • end Завершает объявление\определение любого класса, категории или протокола
  • @private Ограничивает область видимости инвариантов класса методами класса (аналогично С++)
  • protected Стоит по умолчанию. Ограничивает область видимости инвариантов класса методами класса и методами производных классов (аналогично С++)
  • @public Удаляет ограничения на облать видимости (аналогично С++)
  • try Определяет блок с возможной генерацией исключений (аналогично С++)
  • @throw Генерирует объект-исключение (аналогично С++)
  • catch () Обрабатывает исключение, сгенерированное в предшествующем блоке try (аналогично С++)
  • finally Определяет блок после блока try, в который предается куправление независимо от того, было или нет сгенерировано исключение
  • @class Сокращенная форма объявления класса (только имя (аналогично С++))
  • selector(method_name) Возвращает скомпилированный селектор для имени метода method_name
  • @protocol(protocol_name) Ворзвращает экземпляр класса-протокола с именем protocol_name
  • @encode(type_spec) Инициализирует строку символов, которая будет использована для шифрования данных типа type_spec
  • @synchronized() Определяет блок кода, выполняющегося только одной нитью в любой определенный момент времени

Обмен сообщениями

Чтобы заставить объект выполнить какой-нибудь метод нужно послать ему сообщение, именуемое так же, как и требуемый метод. Такое сообщение называется селектор метода. Синтаксис посылки таков:

[receiver method];

В сообщении можно передавать параметры для вызываемого метода:

[receiver method: 20.0 : 30.0];

Перед каждым параметром необходимо ставить двоеточие. Сколько двоеточий – столько и параметров. Имя метода может продолжаться после каждого такого двоеточия-параметра:

[receiver methodWithFirstArgument: 10 andSecondArgument: 20];

Методы с неограниченным количством аргументов вызываюся следующим синтаксисом:

[receiver undefinedNumberParameters: one, two, three, four, five, six, seven];

Посылка сообщения, как и любая функция C, возвращает определенное (может void) значение:

BOOL booleanValue;
booleanValue = [reveiver method];

При посылке сообщения nil оно просто пропадает. При посылке сообщения объекту, который принадлежит классу, не реализовавшему заказанный метод, возникает исключение, которое, будучи не перехваченным, приводит всю программу к незапланированному завершению. Для проверки, отвечает ли данный объект на кокое-либо сообщение можно использовать следующий шаблон кода:

if ([anObject respondsToSelector: @selector(myMethodWith2Argumets::)])
{
//можно вызывать
[anObject myMethodWith2Argumetns: @”first” : @”second”];
}
else
{
//ни в коем случае не вызывать
}

Как работает передача сообщений

Посылка сообщения транслируется в С-функцию с прототипом:

id objc_msgSend(id receiver, SEL method, ...);

Тип SEL, по сути, определен как char const *, но лучше воспринимать его как int, поскольку во время выполнения все селекторы индексируются целыми значениями согласно глобальной таблице селекторов.

Пользуясь инвариантом isa объекта receiver (при использовании фреймворка Foundation, базового для Cocoa, все классы должны наследовать класс NSObject, поэтому наличие isa неизбежно), эта функция просматривает локальный список селекторов класса с целью определить, отвечает ли объект данного класса на сообщение method. Если такой селектор находится, то управление передается соответствующему методу класса, которому передается id объекта (указатель на его инварианты) и указанные после селектора параметры функции objc_msgSend(). Значение, возвращенное методом, отдается как результат посылки сообщения. Если у объекта-приемника данный селектор отсутствует, функции objc_msgSend() просматривает список селекторов его базового класса.

При такой схеме вызов, например:

[receiver аddObject: otherObject];

Транслируется в:

objc_msgSend(receiver, 12, otherObject);

Так как в глобальной таблице селекторов 12 соответствует строке “addObject:”. Далее функция objc_msgSend() выполняет поиск по списку селекторов объекта receiver и, найдя его (пусть это объект класса NSArray, который реализовал метод с селектором 12), производит вызов типа:

addObject(receiver, otherObject);

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

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

- (void)addObject: (id)otherObject;

То есть принимал всего один параметр. Но, исходя из принципа объектно-ориентированной парадигмы, что методы – это подпрограммы, обрабатывающие определенные наборы данных, методу необходимо передавать адресс данных, подлежащих обработке. Поэтому такой параметр передается во всякий метод класса неявно. Компилятору об этом дополнительном параметре дает понять минус («-«), стоящий первым в прототипе метода. Такой метод (с минусом впереди) называется методом объекта (или экземпляра), т.к. может быть вызван только для объекта какого-нибудь класса. В теле метода этот указатель на экземпляр данных (или адрес объекта, которому послали сообщение) доступен посредством зарезервированного слова self (аналог this в С++), а указатель на экземпляр базового класса – через зарезервированное слово super. Кроме того, в метод объекта также передается неявный параметр _cmd – селектор этого метода из глобальной таблицы селекторов. С точки зрения программиста С++ все методы объектов в Objective-C как-будто объявлены с ключевым словом virtual, и всегда следуют динамическому полиморфизму.

Если в начале прототипа метода поставить знак плюс (“+”), то такой метод будет считаться методом класса, и, естественно, не будет принимать неявный параметр self (это аналогично объявлению static-метода в С++). А без инварианта isa объекта, на который указывает self, указатель super работать, конечно, тоже не будет.
Таким образом, прототип любого метода объявляется так:

-|+ (<тип возвращаемого значения>) основнаяЧастьИмениМетода
[ : (<тип первого параметра>)имяПервогоФормальногоПараметра
[ [дополнительнаяЧастьИмениМетода] : (<тип второго параметра>)имяВторогоФормальногоПараметра]
… ]

Например:

+ (Class)class;
+ (id)alloc;
- (id)init;
- (void)addObject: (id)anObject;
+ (NSString *)stringWithCString: (const char*)aCString usingUncoding: (enum NSStringEncoding)encoding;
- (NSString *)initStringWithFormat: (NSString *)format, …;

Если метод возвращает некий объект (тип id) или класс (тип Class), можно воспользоваться вложенным синтаксисом вызова:

[myLabel setText: [[NSString stringWithString: @”Hello”] stringByAppendingString: @” world”]];

Здесь объекту класса UILabel из фреймворка UIKit устанавливается значение инварианта text равное строке @”Hello world”. Эта строка, в свою очередь, образована конкатенацией строк @”Hello” и @” world”. Первая является результатом посылке сообщения stringWithString классу NSString с параметром-константой @”Hello”. Такой вызов возвращает объект класса NSString, инициализированный строкой-параметром. Затем этому объекту посылается сообщение stringByAppendingString с параметром @” world”. Результат посылки этого сообщения и есть объект класса NSString, содержащий конкатенацию значения объекта-приемника и строкового аргумента. Этот объект и попадает как параметр в сообщение setText: объекта myLabel.

Объявление класса

Объявим простой класс комплексного числа в файле Complex.h:

#import <Foundation/Foundation.h> //для NSObject и строк NSString

@interface Complex : NSObject
{
double _re; //инвариант для действительной части
double _im; //инвариант для мнимой части
NSString *_format; //строка формата для метода description
}
- (id)initWithRe: (double)re andIm: (double)im; //специализированный конструктор
+ (Complex *)complexWithRe: (double)re andIm: (double)im; //метод класса для одноэтапного создания объекта
- (Complex *)add: (Complex *)other; //метод для сложения
- (Complex *)sub: (Complex *)other; //метод для вычетания
- (NSString *)format; //метод доступа к _format
- (void)setFormat: (NSString *)format; //метод установки _format
- (double)re; //остальные методы доступа к действительной и мнимой частям
- (void)setRe: (double)re;
- (double)im;
- (void)setIm: (double)im;
@end

Как видим, все объявление заключено в ключевые слова interface и end. Первым делом объявляются инварианты (в фигурных скобках). Вне фигурных скобок объявляются методы. Метод description отсутствует в объявлении класса не случайно. Дело в том, что он, как и метод dealloc и init, присутствует в определении класса. При посылке объекту класса Complex сообщения description будет рассмотрен его локальный список селекторов, куда, после компиляции, попадут селекторы всех методов, реализованных классом этого объекта, даже не объявленные в интерфейсной части. То есть init, description и dealloc будут вызывать абсолютно корректно.

Создание объектов

В связи с тем, что все объекты распределяютя в динамической памяти, cоздание объекта приходится проводить в два этапа: 1) выделении памяти (сообщение alloc) и 2) инициализация инвариантов (конструкторы класса).

MyClass *myObject = [[MyClass alloc] init];  //метод класса MyClass alloc выделяет участок памяти нужного размера и возвращает указатель на него, метод объекта init инициализирует инварианты объекта myObject

После создания объекта им можно смело пользоваться:

NSMutableArray *array = [[NSMutableArray alloc] init]; //создаем изменяемый массив
MyClass *myObject = [[MyClass alloc] init]; //наш объект
[myObject myMethod]; //посылка некоторого сообщения
[array addObject: myObject]; //помещаем объект в массив
MyClass *otherObject = [array getLastObject:]; //достаем его из массива, указываем на него другим указателем
[otherObject myOtherMethod: YES]; //посылаем ему другое сообщение с аргументом типа BOOL

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

+ (NSString *)stringWithCString: (char const *)string encoding: (NSStringEncoding)encoding;

Возвращает уже готовую строку, инициализированную соответствующей сторокой с завершающим нулем, без вызовов alloc и init:

NSString *myString = [NSString stringWithCString: “Bla-bla-bla” encoding: NSASCIIStringEncoding];

Время жизни объекта

Как только указатель на объект выходит за свою область видимости, память, выделенная под него, безвозвратно теряется (если, конечно, это был последний указатель на тот объект) и происходит утечка. Дабы избежать таких нежелательных последствий в Objective-C поддерживается парадигма подсчета ссылок на ресурсы. Таким образом, у каждого объекта есть целочисленный счетчик, который показывает количество ссылающихся на него указателей. По достижению этим счетчиком нуля, память, выделенная для данного объекта, возвращается системе. После вызова метода класса alloc, этот счетчик равен единице. Чтобы увеличить его значение необходимо послать объекту сообщение retain, а чтобы уменьшить – release. Все эти методу реализует NSObject, который любой наш класс непременно наследует. Интересно отметить, что значение счетчика для статических объектов класса NSString (например @”I am a string”) равно -1, то есть максимально возможное. Вот пример работы со счетчиком:

id anObject = [SomeClass alloc]; //вначале счетчик == 1
[anObject init]; //тут создаются инварианты объекта
[anObject reatin]; //увеличим его значение (теперь он == 2)
[anObject release]; //уменьшим (счетчик опять == 1 и объект по прежнему жизнеспособен)
[anObject release]; //счетчик обнуляется, уменьшаются на 1 счетчики инвариантов и выделенная под объект память возвращается ОС

Реализация init очень важна. Это конструктор класса. Конструкторы отличаются тем, что возвращаеют id и их названия всегда начинается со слова init, а конструктор по умолчанию – это и есть просто init. Схема любого конструктора примерно следующая:

- (id)init
{
self = [super init]; //вызываем конструктор базового класса для
//инициализации его инвариантов
if (self) //если в конструкторе базового класса все прошло удачно
//и он вернул корректный объект, а не освободив память вернул nil
{
//то тут можно смело инициализировать свои инварианты
}
return self; //и возвращать самого себя
}

Вот типичный специализированный (не по умолчанию) конструктор для класса с двумя членами типа некоторого класса и одним целочисленным инвариантом:

- (id)initWithInt: (int)number
{
if (self = [super init])
{
_myMember1 = [[SomeClass alloc] init]; //все как положено: выделили память, затем ее инициализировали
_myMember2 = [[SomeClass alloc] init];
_myIntMember = number; //здесь конструктор ни к чему
//инициализируем переданным параметром
}
return self;
}

Реализация release и retain для NSObject идеологически примерно следующая, и ее не нужно переопределять в производных классах, в силу отсутствия доступа к инварианту счетчика ссылок:

- (void)retain
{
[_internalLock lock]; //блокировка для синхронизации
_referenceCounter++; // пусть _referenceCounter – скрытый инвариант счетчика
[_internalLock unlock];
}
- (void)release
{
[_internalLock lock];
_referenceCounter--; //уменьшим счетчик
if (!_referenceCounter) //если он равен нулю
{
[_internalLock unlock];
[self dealloc]; //скажем себе, что пора умирать (блокировка освободится тут)
}
[_internalLock unlock];
}

То есть самому объекту посылается сообщение dealloc, в реализации метода которого он может, по необходимости, уменьшить счетчики своих инвариантов и передать аналогичное сообщение объекту базового класса, чтобы он сделал то же самое. Очевидно, реализация метода dealloc для NSObject освободит память, выделенную объекту. Обычно dealloc для какого-нибудь класса выглядит так:

- (void)dealloc
{
[_myMember1 release]; //уменьшим счетчик своего инварианта 
[_myMember2 release]; //уменьшим счетчик другого своего инварианта
//[_myIntMember release]; это полный бред, т.к. встроенные типы сообщений не принимают вообще и счетчиков не ведут
[super dealloc]; //cкажем объекту базового класса, что пора освобождать память
}

Методы доступа

Правильная работа с подсчетом ссылок очень важна при возврате адреса объекта из метода или инициализации инварианта формальным параметром. Обычно такими вещами занимаются так называемые методы доступа, возвращающие и устанавливающие инварианты объектов. Принято именовать метод, возвращающий значение инварианта, так же как и инвариант, а имя метода, устанавливающего его значение, начинать со слова set:

- (void)setRe: (double)re
{
_re = re;
}

Так как инвариант _re относится ко встроенному типу, никаких сложностей с изменением его значения не возникает. Но если инвариант – объект некоторого класса – то простым присваиванием не обойтись, ведь надо учитывать счетчики ссылок. Для решения этой проблемы применяются следующие три метода:

//например, нужно изменить текст у ярлыка
[label setText: @”Hello world”]; //устанавливаем инвариант text
//объекта label равным текстовой константе типа NSString *

//примерная реализация setText в классе UILabel (вариант №1)
- (void)setText: (NSString *)text
{
[text retain]; //увеличиваем счетчик ссылок на формальный параметр
[_text release]; //уменьшаем счетчик ссылок текущего значения своего инварианта _text
_text = text; //инициализируем инвариант новым значением
}

//примерная реализация setText в классе UILabel (вариант №2)
- (void)setText: (NSString *)text
{
if (_text != text) //cравниваем указатели на объекты
{
[_text release]; //уменьшаем счетчик ссылок текущего значения
//своего инварианта _text
_text = [text retain]; //увеличиваем счетчик ссылок
//на формальный параметр и инициализируем свой инвариант
}
}

//примерная реализация setText в классе UILabel (вариант №3 – нежелательный)
- (void)setText: (NSString *)text
{
if (_text != text)
{
[_text autorelease]; //скинем текущеe значения своего
//инварианта _text в самовыгружаемый пул
_text = [text retain]; //увеличиваем счетчик ссылок
//на формальный параметр и инициализируем свой инвариант
}
}

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

- (NSString *)text
{
return _text;
}

Самовыгружаемый пул в нитях программы

Теперь попробуем вернуть из метода созданный внутри него объект:

-(NSString *)sayHelloToName: (NSString *)name withSurname: (NSString *)surname 
{
NSString *retString = [[NSString alloc] initWithFormat: @”%@ %@!”, name, surname]; //инициализируем созданный объект посредством строки формата
return retString;
}

Строка формата соответствует стандарту языка С. Но если в ней необходимо указать тип id, то используется спецификатор формата %@. Каким образом метод, разбирающий формат, понимает какие символы подставить вместь id? Он просто подставит то, что вернет метод описания description данного объекта. Этот метод изначально объявлен для класса NSObject. NSString переопределил его на вывод своего строкового содержания. Переопределив его, любой объект может представлять свое строковое содержание. Например, так это может сделать класс комплексного числа с двумя инвариантами типа double:

- (NSString *)description
{
return [NSString stringWithFormat: @”re: %lf im: %lf”, _re, _im]; //возвращает строку @“re: 1.0 im: 2.5” для _re == 1.0 и _im == 2.5
}

После выполнения метода sayHelloToName:withSurname: определенно произойдет утечка памяти, так как вызывающий код скорей всего не догадывается, что возвращенному объекту нужно после обработки послать сообщение release. Даже если он догадается это сделать, возможно, что возвращался указатель на инвариант объекта, а значит его уничтожение чревато серьезными последствиями. Хотелось бы иметь механизм самоосвобождения объектов когда либо в будующем, чтобы пользовательский код вообще не думал об их освобождении. Решается эта проблема с помощью объекта класса NSAutoreleasePool – самовыгружаемого пула объектов.

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

Любая нить в программе, использующей Cocoa, должна создавать объект класса NSAutoreleasePool в самом начале (прежде создания других объектов), и в самом конце его уничтожать (после уничтожения всех других объектов). Функция main(), являющаяся главной нитью любой программы на Objective-C, при использовании фреймворка Cocoa должна всегда выглядеть вот так:

int main(int argc, char *argv[]) // или же просто main()
{ 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //создаем пул, он автоматически становится текущим
int retVal;
//теперь можно программировать спокойно
[pool drain]; //освобождаем пул и все объекты, помещенные в него вызовами autorelease
return retVal;
}

А корректный метод sayHelloToName:withSurname: теперь будет выглядеть вот так:

-(NSString *)sayHelloToName: (NSString *)name withSurname: (NSString *)surname 
{
NSString *retString = [[NSString alloc] initWithFormat: @”%@ %@!”, name, surname]; //инициализируем созданный объект посредством строки формата
[retString autorelease]; //помещаем в пул, теперь retString освободится вместе с пулом
return retString;
}

К слову, метод drain самовыгружаемого пула аналогичен release с той лишь разницей, что, кроме освобождения себя самомго и всех содержащихся объектов, еще дает подсказку сборщику мусора вступить в игру. Однако, это актуально только для Mac OS 10.4 и выше, так как на iOS сборки мусора нет.

Определение класса

Теперь рассмотрим файл Complex.m с определением методов класса Complex:

#import “Complex.h”

@implementation Complex
- (id)init
{
return [self initWithRe: 0.0 andIm: 0.0];
}
- (id)initWithRe: (double)re andIm: (double)im
{
if (self = [super init])
{
_re = re;
_im = im;
_format = @”re: %.1lf im: %.1lf”; //формат вывода по умолчанию
}
}
+ (Complex *)complexWithRe: (double)re andIm: (double)im
{
return [[[Complex alloc] initWithRe: re andIm: im] autorelease];
}
- (Complex *)add: (Complex *)other
{
return [[Complex alloc] initWithRe: _re + other->_re andIm: _im + other->_im];
}
- (Complex *)sub: (Complex *)other
{
return [[Complex alloc] initWithRe: _re – other->_re andIm: _im – other->_im];
}
- (NSString *)format
{
return _format;
}
- (void)setFormat: (NSString *)format
{//стандартный порядок действий для инварианта-объекта
[format retain];
[_format release];
_format = format;
}
- (double)re
{
return _re;
}
- (void)setRe: (double)re
{
_re = re;
}
- (double)im
{
return _im;
}
- (void)setIm: (double)im
{
_im = im;
}
- (NSString *)description
{//используем установленный формат вывода
return [NSString stringWithFormat: _format, _re, _im]; 
}
- (void)dealloc
{
[_format release]; //для этого и переопределялся dealloc
[super dealloc];
}
@end

Конструктор по умолчанию вызывает специализированный конструктор с определенными начальными параметрами. Метод complexWithRe:andIm: возвращает инициализированный объект класса Complex, размещенный в текущем самовыгружаемом пуле. То же самое делает и метод description, возвращая объект класса NSString. Вот пример программы, где используется класс Complex:

#import “Complex.h”
#import <stdio.h>  //для printf()

int main()
{ 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Complex *num1 = [[Complex alloc] init]; //0.0+0.0*i
Complex *num2 = [[Complex alloc] initWithRe: 1.5 andIm: -2];
//1.5-2.0*i
Complex *num3 = [Complex complexWithRe: 5 andIm: 7];
//5.0+7.0*i
printf(“%s\n”, [[num2 description] cStringUsingEncoding: NSASCIIStringEncoding]); //вывод> re: 1.5 im: -2.0
printf(“%s\n”, [[[num2 add: num3] description] cStringUsingEncoding: NSASCIIStringEncoding]); //вывод> re: 6.5 im: 5.0
[num1 setRe: [num2 re]]; //задаем _re для num1 как у num2
[num1 setIm: [num3 im]]; //задаем _im для num1 как у num3
[num1 setFormat: @”%.2lf+%.2lf*i”]; //меняем формат вывода для num1
printf(“%s\n”, [[num1 description] cStringUsingEncoding: NSASCIIStringEncoding]); //вывод> 1.50+7.00*i
[num1 release];
[num2 release];
//[num3 release]; не нужно, т.к. он уже в самовыгружаемом пуле
[pool drain];
return 0;
}

Категории и расширения

Если к уже написанному (а, возможно, и откомпилированному) классу нужно добавить\переопределить некоторые методы без наследования – категории позволяют это сделать без особых усилий:

//файл “CategorizedComplex.h”

#import “Complex.h”
@interfce Complex (CategorizedComplex)
- (Complex *)mul: (Complex *)other;
- (Complex *)div: (Complex *)other;
@end

//файл “CategorizedComplex.m”
#import “CategorizedComplex.h”

@implementation Complex (CategorizedComplex)
- (Complex *)mul: (Complex *)other
{
return [Complex complexWithRe: _re * other->_re - _im * other->_im andIm: _re * other->_im + _im * other->_re];
}
- (Complex *)div: (Complex *)other
{
double retRe, retIm, denominator;
denominator = other->_re * other->_re + other->_im * other->_im;
if (!denominator)
return nil;
retRe = (_re * other->_re + _im * other->_im) / denominator;
retIm = (_im * other->_re - _re * other->_im) / denominator;
return [Complex complexWithRe: retRe andIm: retIm];
}
@end

А пользоваться этим можно вот так:

CategorizеdComplex *num1 = [[CategorizedComplex alloc] initWithRe: 1 andIm: 999];
Complex *num2 = [Complex complexWithRe: 0 andIm: 0];
CategorizedComplex *num3 = [num1 div: num2]; //num3 == nil

Расширения несут добрую службу как безымянные категории:

//файл “CategorizedComplex.m”
#import “CategorizedComplex.h”
@interface Complex ()
- (void)zeroComplex; //тайный метод для обнуления числа
@end

@implementation Complex
- (void)zeroComplex //им могут пользоваться только методы самого класса
{
_re = 0;
_im = 0; 
}
@end

Протоколы

Протокол Objective-C – это формализованное объявление группы методов, которые, по желанию, может реализовать любой класс (аналог класса в С++, где все методы объявлены со спецификатором virtual … = 0). В версии языка 2.0 методы протокола могут быть требуемыми (спецификатор @required, он считается умалчиваемым) и выборочными (спецификатор @optional). Если какой либо класс реализовал требуемые методы протокола, то он называется классом, поддерживающим данный протокол. Протокол, и класс, его поддерживающий, объявляются вот так:

@protocol MyPrinterProtocol
@required
- (void)print;
- (BOOL)switchedOn;
@optional
- (void)loadPapaer: (int)numberOfPages;
@end

@interface MyPrinter : NSObject <MyPrinterProtocol>
//теперь MyPrinter реализует методы MyPrinterProtocol
{
BOOL _state;
int _numberOfPages;
}
- (id)initWithState: (BOOL)state andPagesCount: (int)pages;
- (BOOL)state;
@end

Oбъекту класса MyPrinter можно гарантированно посылать сообщения print и switchedOn, и, после проверки на respondsToSelector:, можно посылать сообщение loadPaper:, та как в его реализации должны присутствовать определения одноименных методов. Объявление объекта класса, поддерживающего какой-либо протокол осуществляется так:

MyPrinter *printer;
id anotherPrinter = [[MyPrinter alloc] init];
[anotherPrinter print]; //безымянный объект отвечает на сообщение без предупреждений компилятора

Кроме того, один класс может удовлетворять нескольким протоколам. Для этого их можно перечислить через запятую в угловых скобках в объявлении класса.

@interface MyPrinter : NSObject <MyPrinterProtocol, OtherProtocol>

А чтобы объявить объект неизвестного класса (id), соответствующий некоторому протоколу, пишут так:

id <MyPrinterProtocol&gt somePrinter;

Исключения

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

Исключение – это объект некоторого класса. Он (даже своим типом) несет в себе некоторую информацию о произошедшей ситуации. Для удобства в Cocoa имеется класс NSException, который можно инициализировать двумя объектами NSString и одним объектом произвольного класса (тип id):

- (id)initWitnName: (NSString *)name reason: (NSString *)reason userInfo: (id)userInfo;

Сгенерировать исключение и, тем самым, запустить механизм раскрутки стека вызовов, можно с помощью оператора @throw. Чтобы перхватить сгенерированное исключение, участок кода, где возможна его генерация, необходимо заключить в специальный блок с заглавием try (такие блоки могут быть вложенными). А затем, после этого блока, поставить блок с заглавием catch(), где в круглых скобках указать тип предполагаемого исключения. Блоков catch() после блока try может быть несколько. После генерации исключения управление, раскручивая стек, выходит из блока try и, проверяя по очереди все блоки catch(), попадает именно в тот блок catch(), в фигурных скобках которого стоит такой тип, к которому тип исключения приводится неявно (точное совпадение, указатель на базовый класс или id). Если исключение по типу не совпало ни с одним блоком catch(), управление продолжает раскрутку стека. Если после блока с заглавием try стоит блок с заглавием finally, то управление передастся ему независимо от того, произошло ли в блоке try исключение (и обработан какой-нибудь блок catch()), или выполнилась его последняя инструкция. Ниже приведен пример работы с объектом класса Cup в методе fill которого происходит исключение:

Cup *cup = [[Cup alloc] init];
@try 
{
[cup fill]; //в fill генерируется исключение типа NSException
}
@catch (NSException *exception)
{//логируем произошедшее исключение с помощью NSLog
NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
}
@finally //сюда после @try мы попадем неизбежно
{
[cup release]; 
}

В блоке finally удобно освобождать ресурсы, выделенные в блоке try, но не освобожденные по причине сгенерированного исключения.

Свойства

Для версии Objective-C 2.0 нашa реализация класса Complex явно избыточна: в ней слишком много методов доступа и их определение – сплошная рутина. Перепишем его с использованием свойств:

//файл “Complex.h”
#import <Foundation/Foundation.h>  //для NSObject и строк NSString

@interface Complex : NSObject
{
double _re; //инвариант для действительной части
double _im; //инвариант для мнимой части
NSString *_format; //строка формата для метода description
}
- (id)initWithRe: (double)re andIm: (double)im; 
+ (Complex *)complexWithRe: (double)re andIm: (double)im; 
- (Complex *)add: (Complex *)other; //метод для сложения
- (Complex *)sub: (Complex *)other; //метод для вычетания
@property (nonatomic, retain) NSString *format; //объявим методы доступа
@property (nonatomic, assign) double re; //посредством объявления свойств
@property (nonatomic, assign) double im;
@end

//файл “Complex.m”
#import “Complex.h”

@implementation Complex
@synthesize format = _format; //сгенерируем методы доступа
@synthesize re = _re; //и заодно переменуим их
@synthesize im = _im; //чтобы в имени не было подчеркивания 
- (id)init
{
return [self initWithRe: 0.0 andIm: 0.0];
}
- (id)initWithRe: (double)re andIm: (double)im
{
if (self = [super init])
{
_re = re;
_im = im;
_format = @”re: %.1lf im: %.1lf”; //формат вывода по умолчанию
}
}
+ (Complex *)complexWithRe: (double)re andIm: (double)im
{
return [[[Complex alloc] initWithRe: re andIm: im] autorelease];
}
- (Complex *)add: (Complex *)other
{
return [[Complex alloc] initWithRe: _re + other.re andIm: _im + other.im]; //используем свойства re и im
}
- (Complex *)sub: (Complex *)other
{
return [[Complex alloc] initWithRe: _re – other.re andIm: _im – other.im]; //используем свойства re и im
}
@end

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

  • getter=getterName, setter=setterName Указывает, что метод доступа для чтения будет называться getterName, а для изменения — setterName
  • readonly Не генерировать метод доступа для изменения
  • readwrite Генерировать оба метода доступа
  • assign Метод доступа на изменение реализовывать посредством простого присваивания
  • retain Принимаемому значению послать сообщение retain, предыдущему значению инварианта послать release и присвоить ему принимаемое значение
  • copy Использовать обычный оператор присваивания, но присвоить копию принимаемого значения (перед присваиванием емупосылается сообщение copy)
  • nonatomic Не использовать внутренние блокировки для синхронизации нескольких нитей в сгенерированных методах доступа (по умолчанию cинхронизация используется)

Теперь в определении класса Complex нам не нужно вручную писать методы доступа. Они сгенерируются компилятором и будут идентичны тем, что были раньше.

Удачи!

Objective-C Обзор

Objective-C – это язык общего назначения, который разработан поверх языка программирования C путем добавления функций языка программирования Small Talk, что делает его объектно-ориентированным языком. Он в основном используется при разработке операционных систем iOS и Mac OS X, а также его приложений.

Первоначально Objective-C был разработан NeXT для ее ОС NeXTSTEP, от которой Apple перешла к его iOS и Mac OS X.

Объектно-ориентированное программирование

Полностью поддерживает объектно-ориентированное программирование, включая четыре столпа объектно-ориентированной разработки –

  • Инкапсуляция
  • Скрытие данных
  • наследование
  • Полиморфизм

Пример кода

Live Demo

#import <Foundation/Foundation.h>

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

   NSLog (@"hello world");
   [pool drain];
   return 0;
}

Основа основы

Foundation Framework предоставляет большой набор функций, и они перечислены ниже.

  • Он включает в себя список расширенных типов данных, таких как NSArray, NSDictionary, NSSet и так далее.

  • Он состоит из богатого набора функций, управляющих файлами, строками и т. Д.

  • Он предоставляет функции для обработки URL, такие утилиты, как форматирование даты, обработка данных, обработка ошибок и т. Д.

Он включает в себя список расширенных типов данных, таких как NSArray, NSDictionary, NSSet и так далее.

Он состоит из богатого набора функций, управляющих файлами, строками и т. Д.

Он предоставляет функции для обработки URL, такие утилиты, как форматирование даты, обработка данных, обработка ошибок и т. Д.

Цель обучения-C

Самое важное, что нужно сделать при изучении Objective-C, – это сосредоточиться на концепциях, а не потеряться в технических деталях языка.

Цель изучения языка программирования – стать лучшим программистом; то есть, чтобы стать более эффективным в разработке и внедрении новых систем и в поддержании старых.

Использование Objective-C

Objective-C, как упоминалось ранее, используется в iOS и Mac OS X. Он имеет большую базу пользователей iOS и значительно увеличивает пользователей Mac OS X. А поскольку Apple в первую очередь ориентируется на качество, и это замечательно для тех, кто начал изучать Objective-C.

Настройка среды Objective-C

Настройка локальной среды

Если вы все еще хотите настроить свою среду для языка программирования Objective-C, вам потребуются следующие две программы, доступные на вашем компьютере: (а) текстовый редактор и (б) компилятор GCC.

Текстовый редактор

Это будет использоваться для ввода вашей программы. Примерами немногих редакторов являются Блокнот Windows, команда редактирования ОС, Brief, Epsilon, EMACS и vim или vi.

Имя и версия текстового редактора могут различаться в разных операционных системах. Например, Блокнот будет использоваться в Windows, а vim или vi могут использоваться в Windows, а также в Linux или UNIX.

Файлы, которые вы создаете в редакторе, называются исходными файлами и содержат исходный код программы. Исходные файлы для программ Objective-C обычно называются с расширением « .m ».

Перед началом программирования убедитесь, что у вас есть один текстовый редактор, и у вас достаточно опыта, чтобы написать компьютерную программу, сохранить ее в файле, скомпилировать и, наконец, выполнить.

Компилятор GCC

Исходный код, написанный в исходном файле, является удобочитаемым исходным кодом для вашей программы. Его нужно «скомпилировать», чтобы он превратился в машинный язык, чтобы ваш процессор мог фактически выполнить программу в соответствии с приведенными инструкциями.

Этот компилятор GCC будет использоваться для компиляции вашего исходного кода в конечную исполняемую программу. Я предполагаю, что у вас есть базовые знания о компиляторе языка программирования.

Компилятор GCC доступен бесплатно на различных платформах, а процедура установки на различных платформах описана ниже.

Установка в UNIX / Linux

Первым шагом является установка gcc вместе с пакетом gcc Objective-C. Это сделано с помощью –

$ su - 
$ yum install gcc
$ yum install gcc-objc

Следующим шагом является настройка зависимостей пакетов с помощью следующей команды –

$ yum install make libpng libpng-devel libtiff libtiff-devel libobjc 
   libxml2 libxml2-devel libX11-devel libXt-devel libjpeg libjpeg-devel

Чтобы получить все возможности Objective-C, загрузите и установите GNUStep. Это можно сделать, загрузив пакет с http://main.gnustep.org/resources/downloads.php .

Теперь нам нужно перейти в загруженную папку и распаковать файл с помощью –

$ tar xvfz gnustep-startup-.tar.gz

Теперь нам нужно переключиться на папку gnustep-startup, которая создается с помощью –

$ cd gnustep-startup-<version>

Далее нам нужно настроить процесс сборки –

$ ./configure

Тогда мы можем построить –

$ make

Мы должны, наконец, настроить окружающую среду путем –

$ . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh

У нас есть helloWorld.m Objective-C следующим образом –

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   
   NSLog (@"hello world");
   [pool drain];
   return 0;
}

Теперь мы можем скомпилировать и запустить файл Objective-C, скажем helloWorld.m, переключившись на папку, содержащую файл, используя cd, а затем выполнив следующие шаги:

$ gcc `gnustep-config --objc-flags` 
-L/usr/GNUstep/Local/Library/Libraries 
-lgnustep-base helloWorld.m -o helloWorld
$ ./helloWorld

Мы можем увидеть следующий вывод –

2013-09-07 10:48:39.772 tutorialsPoint[12906] hello world

Установка в Mac OS

Если вы используете Mac OS X, самый простой способ получить GCC – это загрузить среду разработки Xcode с веб-сайта Apple и следовать простым инструкциям по установке. После настройки XCode вы сможете использовать компилятор GNU для C / C ++.

Xcode в настоящее время доступен по адресу developer.apple.com/technologies/tools/ .

Установка на Windows

Чтобы запустить программу Objective-C в Windows, нам нужно установить MinGW и GNUStep Core. Оба доступны по адресу https://www.gnu.org/software/gnustep/windows/installer.html .

Сначала нам нужно установить системный пакет MSYS / MinGW. Затем нам нужно установить пакет GNUstep Core. Оба из которых предоставляют установщик Windows, который не требует пояснений.

Затем использовать Objective-C и GNUstep, выбрав Пуск -> Все программы -> GNUstep -> Оболочка

Перейдите в папку, содержащую helloWorld.m

Мы можем скомпилировать программу, используя –

$ gcc `gnustep-config --objc-flags` 
-L /GNUstep/System/Library/Libraries hello.m -o hello -lgnustep-base -lobjc

Мы можем запустить программу с помощью –

./hello.exe

Мы получаем следующий вывод –

2013-09-07 10:48:39.772 tutorialsPoint[1200] hello world

Структура программы Objective-C

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

Пример Objective-C Hello World

Программа Objective-C в основном состоит из следующих частей –

  • Команды препроцессора
  • Интерфейс
  • Реализация
  • метод
  • переменные
  • Заявления и выражения
  • Комментарии

Давайте посмотрим на простой код, который будет печатать слова «Hello World» –

Live Demo

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass

- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

@end

int main() {
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   return 0;
}

Давайте посмотрим различные части вышеуказанной программы –

  • Первая строка программы #import <Foundation / Foundation.h> – это команда препроцессора, которая указывает компилятору Objective-C включить файл Foundation.h перед тем, как перейти к фактической компиляции.

  • Следующая строка @interface SampleClass: NSObject показывает, как создать интерфейс. Он наследует NSObject, который является базовым классом всех объектов.

  • Следующая строка – (void) sampleMethod; показывает, как объявить метод.

  • Следующая строка @end отмечает конец интерфейса.

  • Следующая строка @implementation SampleClass показывает, как реализовать интерфейс SampleClass.

  • Следующая строка – (void) sampleMethod {} показывает реализацию sampleMethod.

  • Следующая строка @end отмечает конец реализации.

  • Следующая строка int main () – это основная функция, с которой начинается выполнение программы.

  • Следующая строка /*…*/ будет игнорироваться компилятором, и она была добавлена ​​для добавления дополнительных комментариев в программу. Поэтому такие строки называются комментариями в программе.

  • Следующая строка NSLog (…) – это еще одна функция, доступная в Objective-C, которая вызывает сообщение “Hello, World!” быть отображенным на экране.

  • Следующая строка возвращает 0; завершает функцию main () и возвращает значение 0.

Первая строка программы #import <Foundation / Foundation.h> – это команда препроцессора, которая указывает компилятору Objective-C включить файл Foundation.h перед тем, как перейти к фактической компиляции.

Следующая строка @interface SampleClass: NSObject показывает, как создать интерфейс. Он наследует NSObject, который является базовым классом всех объектов.

Следующая строка – (void) sampleMethod; показывает, как объявить метод.

Следующая строка @end отмечает конец интерфейса.

Следующая строка @implementation SampleClass показывает, как реализовать интерфейс SampleClass.

Следующая строка – (void) sampleMethod {} показывает реализацию sampleMethod.

Следующая строка @end отмечает конец реализации.

Следующая строка int main () – это основная функция, с которой начинается выполнение программы.

Следующая строка /*…*/ будет игнорироваться компилятором, и она была добавлена ​​для добавления дополнительных комментариев в программу. Поэтому такие строки называются комментариями в программе.

Следующая строка NSLog (…) – это еще одна функция, доступная в Objective-C, которая вызывает сообщение “Hello, World!” быть отображенным на экране.

Следующая строка возвращает 0; завершает функцию main () и возвращает значение 0.

Компилировать и выполнять программу Objective-C

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2017-10-06 07:48:32.020 demo[65832] Hello, World!

Objective-C Основной синтаксис

Вы видели базовую структуру программы Objective-C, поэтому вам будет легко понять другие основные строительные блоки языка программирования Objective-C.

Жетоны в Objective-C

Программа Objective-C состоит из различных токенов, и токен является ключевым словом, идентификатором, константой, строковым литералом или символом. Например, следующий оператор Objective C состоит из шести токенов –

NSLog(@"Hello, World! \n");

Отдельные токены –

NSLog
@
(
   "Hello, World! \n"
)
;

Точки с запятой;

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

Например, следующие два разных утверждения –

NSLog(@"Hello, World! \n");
return 0;

Комментарии

Комментарии подобны тексту помощи в вашей программе Objective-C, и они игнорируются компилятором. Они начинаются с / * и заканчиваются символами * /, как показано ниже –

/* my first program in Objective-C */

Вы не можете иметь комментарии в комментариях, и они не встречаются внутри строковых или символьных литералов.

Идентификаторы

Идентификатор Objective-C – это имя, используемое для идентификации переменной, функции или любого другого пользовательского элемента. Идентификатор начинается с буквы от A до Z или от a до z или подчеркивания _, за которым следуют ноль или более букв, подчеркиваний и цифр (от 0 до 9).

Objective-C не позволяет использовать знаки препинания, такие как @, $ и% в идентификаторах. Objective-C является регистрозависимым языком программирования. Таким образом, рабочая сила и рабочая сила – это два разных идентификатора в Objective-C. Вот несколько примеров допустимых идентификаторов:

mohd       zara    abc   move_name  a_123
myname50   _temp   j     a23b9      retVal

Ключевые слова

В следующем списке показано несколько зарезервированных слов в Objective-C. Эти зарезервированные слова не могут использоваться как константы или переменные или любые другие имена идентификаторов.

авто еще долго переключатель
перерыв перечисление регистр ЬурейеЕ
дело внешний вернуть союз
голец поплавок короткая неподписанный
Const за подписанный недействительным
Продолжить идти к размер летучий
дефолт если статический в то время как
делать ИНТ структура _Packed
двойной протокол интерфейс реализация
NSObject NSInteger NSNumber CGFloat
имущество неатомический; сохранить сильный
слабый unsafe_unretained; читай пиши только для чтения

Пробелы в Objective-C

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

Пробел – это термин, используемый в Objective-C для описания пробелов, вкладок, символов новой строки и комментариев. Пробелы отделяют одну часть оператора от другой и позволяют компилятору определить, где заканчивается один элемент в выражении, например int, и начинается следующий элемент. Поэтому в следующем утверждении –

int age;

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

fruit = apples + oranges;   // get the total fruit

Никаких пробельных символов не требуется между фруктами и = или между = и яблоками, хотя вы можете включить некоторые из них, если хотите, чтобы они были удобочитаемыми.

Типы данных Objective-C

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

Типы в Objective-C можно классифицировать следующим образом:

Sr.No. Типы и описание
1

Основные типы –

Они являются арифметическими типами и состоят из двух типов: (а) целочисленные типы и (б) типы с плавающей точкой.

2

Перечисляемые типы –

Они снова являются арифметическими типами и используются для определения переменных, которым могут быть назначены только определенные дискретные целочисленные значения по всей программе.

3

Тип пустоты –

Спецификатор типа void указывает, что значение недоступно.

4

Производные типы –

Они включают (a) типы указателей, (b) типы массивов, (c) типы структур, (d) типы объединений и (e) типы функций.

Основные типы –

Они являются арифметическими типами и состоят из двух типов: (а) целочисленные типы и (б) типы с плавающей точкой.

Перечисляемые типы –

Они снова являются арифметическими типами и используются для определения переменных, которым могут быть назначены только определенные дискретные целочисленные значения по всей программе.

Тип пустоты –

Спецификатор типа void указывает, что значение недоступно.

Производные типы –

Они включают (a) типы указателей, (b) типы массивов, (c) типы структур, (d) типы объединений и (e) типы функций.

Типы массивов и типы структур совместно называются агрегатными типами. Тип функции указывает тип возвращаемого значения функции. Мы увидим основные типы в следующем разделе, тогда как другие типы будут рассмотрены в следующих главах.

Целочисленные типы

В следующей таблице приведены подробные сведения о стандартных целочисленных типах с их размерами хранения и диапазонами значений.

Тип Размер хранилища Диапазон значений
голец 1 байт От -128 до 127 или от 0 до 255
без знака 1 байт От 0 до 255
подписанный символ 1 байт От -128 до 127
ИНТ 2 или 4 байта От -32 768 до 32 767 или от -2 147 483 648 до 2 147 483 647
без знака int 2 или 4 байта От 0 до 65 535 или от 0 до 4 294 967 295
короткая 2 байта От -32 768 до 32 767
неподписанный короткий 2 байта От 0 до 65 535
долго 4 байта От -2 147 483 648 до 2 147 483 647
без знака долго 4 байта От 0 до 4 294 967 295

Чтобы получить точный размер типа или переменной на конкретной платформе, вы можете использовать оператор sizeof . Выражение sizeof (тип) возвращает размер хранилища объекта или типа в байтах. Ниже приведен пример получения размера типа int на любой машине:

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSLog(@"Storage size for int : %d \n", sizeof(int));
   return 0;
}

Когда вы компилируете и запускаете вышеупомянутую программу, она дает следующий результат в Linux:

2013-09-07 22:21:39.155 demo[1340] Storage size for int : 4 

Типы с плавающей точкой

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

Тип Размер хранилища Диапазон значений точность
поплавок 4 байта 1,2E-38 до 3,4E + 38 6 десятичных знаков
двойной 8 байт 2,3E-308 до 1,7E + 308 15 десятичных знаков
длинный двойной 10 байт От 3.4E-4932 до 1.1E + 4932 19 десятичных знаков

Заголовочный файл float.h определяет макросы, которые позволяют вам использовать эти значения и другие подробности о двоичном представлении действительных чисел в ваших программах. В следующем примере будет напечатано место для хранения, занятое типом с плавающей запятой, и его значениями диапазона –

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSLog(@"Storage size for float : %d \n", sizeof(float));
   return 0;
}

Когда вы компилируете и запускаете вышеупомянутую программу, она дает следующий результат в Linux:

2013-09-07 22:22:21.729 demo[3927] Storage size for float : 4 

Тип пустоты

Тип void указывает, что значение недоступно. Он используется в трех видах ситуаций –

Sr.No. Типы и описание
1 Функция возвращается как void

В Objective-C существуют различные функции, которые не возвращают значение, или вы можете сказать, что они возвращают void. Функция без возвращаемого значения имеет тип возврата как void. Например, void exit (int status);

2 Аргументы функции как void

В Objective-C существуют различные функции, которые не принимают никаких параметров. Функция без параметра может быть принята как пустая. Например, int rand (void);

В Objective-C существуют различные функции, которые не возвращают значение, или вы можете сказать, что они возвращают void. Функция без возвращаемого значения имеет тип возврата как void. Например, void exit (int status);

В Objective-C существуют различные функции, которые не принимают никаких параметров. Функция без параметра может быть принята как пустая. Например, int rand (void);

В настоящее время тип пустоты может быть вам не понят, поэтому давайте продолжим, и мы рассмотрим эти концепции в следующих главах.

Переменные Objective-C

Переменная – это не что иное, как имя, данное области памяти, которой могут манипулировать наши программы. Каждая переменная в Objective-C имеет определенный тип, который определяет размер и расположение памяти переменной; диапазон значений, которые могут быть сохранены в этой памяти; и набор операций, которые могут быть применены к переменной.

Имя переменной может состоять из букв, цифр и символа подчеркивания. Он должен начинаться либо с буквы, либо с подчеркивания. Прописные и строчные буквы различны, поскольку Objective-C чувствителен к регистру. На основе базовых типов, описанных в предыдущей главе, будут следующие основные типы переменных:

Sr.No. Тип и описание
1

голец

Обычно один октет (один байт). Это целочисленный тип.

2

ИНТ

Наиболее натуральный размер целого числа для машины.

3

поплавок

Значение с плавающей запятой одинарной точности.

4

двойной

Значение с плавающей запятой двойной точности.

5

недействительным

Представляет отсутствие типа.

голец

Обычно один октет (один байт). Это целочисленный тип.

ИНТ

Наиболее натуральный размер целого числа для машины.

поплавок

Значение с плавающей запятой одинарной точности.

двойной

Значение с плавающей запятой двойной точности.

недействительным

Представляет отсутствие типа.

Язык программирования Objective-C также позволяет определять различные другие типы переменных, которые мы рассмотрим в следующих главах, таких как Перечисление, Указатель, Массив, Структура, Объединение и т. Д. В этой главе мы изучим только основные типы переменных.

Определение переменной в Objective-C

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

type variable_list;

Здесь тип должен быть допустимым типом данных Objective C, включая char, w_char, int, float, double, bool или любой определенный пользователем объект и т. Д., А variable_list может состоять из одного или нескольких имен идентификаторов, разделенных запятыми. Некоторые действительные объявления показаны здесь –

int    i, j, k;
char   c, ch;
float  f, salary;
double d;

Линия int i, j, k; оба объявляют и определяют переменные i, j и k; который инструктирует компилятор создавать переменные с именами i, j и k типа int.

Переменные могут быть инициализированы (им присвоено начальное значение) в их объявлении. Инициализатор состоит из знака равенства, за которым следует постоянное выражение:

type variable_name = value;

Вот некоторые примеры:

extern int d = 3, f = 5;    // declaration of d and f. 
int d = 3, f = 5;           // definition and initializing d and f. 
byte z = 22;                // definition and initializes z. 
char x = 'x';               // the variable x has the value 'x'.

Для определения без инициализатора: переменные со статической продолжительностью хранения неявно инициализируются с помощью NULL (все байты имеют значение 0); начальное значение всех других переменных не определено.

Объявление переменных в Objective-C

Объявление переменной дает гарантию компилятору, что существует одна переменная с заданным типом и именем, так что компилятор приступает к дальнейшей компиляции, не требуя полной информации о переменной. Объявление переменной имеет смысл только во время компиляции, компилятору требуется фактическое объявление переменной во время компоновки программы.

Объявление переменной полезно, когда вы используете несколько файлов и определяете свою переменную в одном из файлов, которые будут доступны во время компоновки программы. Вы будете использовать ключевое слово extern для объявления переменной в любом месте. Хотя вы можете объявить переменную несколько раз в вашей программе Objective-C, но она может быть определена только один раз в файле, функции или блоке кода.

пример

Попробуйте следующий пример, где переменные были объявлены сверху, но они были определены и инициализированы внутри основной функции –

Live Demo

#import <Foundation/Foundation.h>

// Variable declaration:
extern int a, b;
extern int c;
extern float f;

int main () {
  /* variable definition: */
  int a, b;
  int c;
  float f;
 
  /* actual initialization */
  a = 10;
  b = 20;
  
  c = a + b;
  NSLog(@"value of c : %d \n", c);

  f = 70.0/3.0;
  NSLog(@"value of f : %f \n", f);
 
  return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-07 22:43:31.695 demo[14019] value of c : 30 
2013-09-07 22:43:31.695 demo[14019] value of f : 23.333334 

Та же концепция применяется к объявлению функции, когда вы предоставляете имя функции во время ее объявления, и ее фактическое определение может быть дано где-либо еще. В следующем примере это объясняется с использованием функции C, и, как вы знаете, Objective-C также поддерживает функции стиля C –

// function declaration
int func();

int main() {
   // function call
   int i = func();
}

// function definition
int func() {
   return 0;
}

L-значения и R-значения в Objective-C

В Objective-C есть два вида выражений:

  • lvalue – Выражения, которые ссылаются на ячейку памяти, называются выражением «lvalue». Lvalue может отображаться как левая или правая сторона задания.

  • rvalue – термин rvalue относится к значению данных, которое хранится по некоторому адресу в памяти. Значение r – это выражение, которому не может быть присвоено значение, что означает, что значение r может появляться с правой, но не с левой стороны присвоения.

lvalue – Выражения, которые ссылаются на ячейку памяти, называются выражением «lvalue». Lvalue может отображаться как левая или правая сторона задания.

rvalue – термин rvalue относится к значению данных, которое хранится по некоторому адресу в памяти. Значение r – это выражение, которому не может быть присвоено значение, что означает, что значение r может появляться с правой, но не с левой стороны присвоения.

Переменные являются lvalues ​​и могут отображаться в левой части назначения. Числовые литералы являются r-значениями, поэтому не могут быть назначены и не могут отображаться слева. Следующее является действительным утверждением –

int g = 20;

Но следующее не является допустимым утверждением и может привести к ошибке во время компиляции –

10 = 20;

Константы Objective-C

Константы относятся к фиксированным значениям, которые программа не может изменить во время своего выполнения. Эти фиксированные значения также называются литералами .

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

Константы обрабатываются как обычные переменные, за исключением того, что их значения не могут быть изменены после их определения.

Целочисленные литералы

Целочисленный литерал может быть десятичной, восьмеричной или шестнадцатеричной константой. Префикс указывает основание или основание: 0x или 0X для шестнадцатеричного, 0 для восьмеричного и ничего для десятичного.

Целочисленный литерал также может иметь суффикс, который представляет собой комбинацию U и L для беззнакового и длинного соответственно. Суффикс может быть в верхнем или нижнем регистре и может быть в любом порядке.

Вот несколько примеров целочисленных литералов –

212         /* Legal */
215u        /* Legal */
0xFeeL      /* Legal */
078         /* Illegal: 8 is not an octal digit */
032UU       /* Illegal: cannot repeat a suffix */

Ниже приведены другие примеры различных типов литералов Integer:

85         /* decimal */
0213       /* octal */
0x4b       /* hexadecimal */
30         /* int */
30u        /* unsigned int */
30l        /* long */
30ul       /* unsigned long */

Литералы с плавающей точкой

Литерал с плавающей точкой имеет целочисленную часть, десятичную точку, дробную часть и экспоненту. Вы можете представлять литералы с плавающей запятой в десятичной или экспоненциальной форме.

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

Вот несколько примеров литералов с плавающей точкой –

3.14159       /* Legal */
314159E-5L    /* Legal */
510E          /* Illegal: incomplete exponent */
210f          /* Illegal: no decimal or exponent */
.e55          /* Illegal: missing integer or fraction */

Символьные константы

Символьные литералы заключаются в одинарные кавычки, например, ‘x’, и могут храниться в простой переменной типа char .

Символьный литерал может быть простым символом (например, «x»), escape-последовательностью (например, «\ t») или универсальным символом (например, «\ u02C0»).

Есть определенные символы в C, когда они обрабатываются обратной косой чертой, они будут иметь особое значение, и они будут использоваться для представления, например, новой строки (\ n) или табуляции (\ t). Здесь у вас есть список некоторых из таких кодов escape-последовательностей –

Последовательность побега Имея в виду
\\ \ персонаж
‘ персонаж
” персонаж
\? ? персонаж
\ а Оповещение или звонок
\ б возврат на одну позицию
\ е Форма подачи
\ п Новая линия
Возврат каретки
\ т Горизонтальная вкладка
\ v Вертикальная вкладка
\ ооо Восьмеричное число от одной до трех цифр
ххх , , Шестнадцатеричное число из одной или нескольких цифр

Ниже приведен пример, показывающий несколько символов escape-последовательности:

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSLog(@"Hello\tWorld\n\n");
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-07 22:17:17.923 demo[17871] Hello	World

Строковые литералы

Строковые литералы или константы заключаются в двойные кавычки “”. Строка содержит символы, похожие на символьные литералы: простые символы, escape-последовательности и универсальные символы.

Вы можете разбить длинную строку на несколько строк, используя строковые литералы и разделяя их пробелами.

Вот несколько примеров строковых литералов. Все три формы являются одинаковыми строками.

"hello, dear"

"hello, \

dear"

"hello, " "d" "ear"

Определение констант

Есть два простых способа определения констант в C:

  • Использование #define препроцессора.

  • Используя ключевое слово const .

Использование #define препроцессора.

Используя ключевое слово const .

Препроцессор #define

Ниже приведена форма для использования препроцессора #define для определения константы:

#define identifier value

Следующий пример объясняет это подробно –

Live Demo

#import <Foundation/Foundation.h>

#define LENGTH 10   
#define WIDTH  5
#define NEWLINE '\n'

int main() {
   int area;
   area = LENGTH * WIDTH;
   NSLog(@"value of area : %d", area);
   NSLog(@"%c", NEWLINE);

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-07 22:18:16.637 demo[21460] value of area : 50
2013-09-07 22:18:16.638 demo[21460] 

Ключевое слово const

Вы можете использовать префикс const для объявления констант определенного типа следующим образом:

const type variable = value;

Следующий пример объясняет это подробно –

Live Demo

#import <Foundation/Foundation.h>

int main() {
   const int  LENGTH = 10;
   const int  WIDTH  = 5;
   const char NEWLINE = '\n';
   int area;  
   
   area = LENGTH * WIDTH;
   NSLog(@"value of area : %d", area);
   NSLog(@"%c", NEWLINE);

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-07 22:19:24.780 demo[25621] value of area : 50
2013-09-07 22:19:24.781 demo[25621] 

Обратите внимание, что это хорошая практика программирования для определения констант в заглавных буквах.

Операторы Objective-C

Оператор – это символ, который указывает компилятору выполнять определенные математические или логические манипуляции. Язык Objective-C богат встроенными операторами и предоставляет следующие типы операторов:

  • Арифметические Операторы
  • Операторы отношений
  • Логические Операторы
  • Битовые операторы
  • Операторы присваивания
  • Разные Операторы

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

Арифметические Операторы

В следующей таблице показаны все арифметические операторы, поддерживаемые языком Objective-C. Предположим, что переменная A содержит 10, а переменная B содержит 20, тогда –

Показать примеры

оператор Описание пример
+ Добавляет два операнда А + Б даст 30
Вычитает второй операнд из первого A – B даст -10
* Умножает оба операнда А * Б даст 200
/ Делит числитель на знаменатель Б / у даст 2
% Оператор модуля и остаток от целочисленного деления B% A даст 0
++ Оператор приращения увеличивает целое значение на единицу А ++ даст 11
Оператор декремента уменьшает целочисленное значение на единицу A– даст 9

Операторы отношений

В следующей таблице показаны все реляционные операторы, поддерживаемые языком Objective-C. Предположим, что переменная A содержит 10, а переменная B содержит 20, тогда –

Показать примеры

оператор Описание пример
== Проверяет, равны ли значения двух операндов или нет; если да, то условие становится истинным. (A == B) не соответствует действительности.
знак равно Проверяет, равны ли значения двух операндов или нет; если значения не равны, то условие становится истинным. (A! = B) верно.
> Проверяет, больше ли значение левого операнда, чем значение правого операнда; если да, то условие становится истинным. (A> B) не соответствует действительности.
< Проверяет, меньше ли значение левого операнда, чем значение правого операнда; если да, то условие становится истинным. (A <B) верно.
> = Проверяет, больше ли значение левого операнда или равно значению правого операнда; если да, то условие становится истинным. (A> = B) не соответствует действительности.
<= Проверяет, меньше ли значение левого операнда или равно значению правого операнда; если да, то условие становится истинным. (A <= B) верно.

Логические Операторы

В следующей таблице приведены все логические операторы, поддерживаемые языком Objective-C. Предположим, что переменная A содержит 1, а переменная B содержит 0, тогда –

Показать примеры

оператор Описание пример
&& Называется логический оператор И. Если оба операнда отличны от нуля, условие становится истинным. (A && B) неверно.
|| Вызывается логическим оператором ИЛИ. Если любой из двух операндов отличен от нуля, условие становится истинным. (A || B) верно.
! Вызывается логическим оператором НЕ. Используйте для изменения логического состояния своего операнда. Если условие истинно, то оператор Логический НЕ будет делать ложь. ! (A && B) верно.

Битовые операторы

Побитовый оператор работает с битами и выполняет побитовую операцию. Таблицы истинности для &, | и ^ следующие:

п Q P & Q р | Q р ^ д
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

Предположим, если А = 60; и B = 13; теперь в двоичном формате они будут выглядеть следующим образом –

A = 0011 1100

B = 0000 1101

—————–

A & B = 0000 1100

A | B = 0011 1101

A ^ B = 0011 0001

~ A = 1100 0011

Побитовые операторы, поддерживаемые языком Objective-C, перечислены в следующей таблице. Предположим, что переменная A содержит 60, а переменная B содержит 13, тогда –

Показать примеры

оператор Описание пример
& Двоичный оператор AND немного копирует результат, если он существует в обоих операндах. (A & B) даст 12, что составляет 0000 1100
| Оператор двоичного ИЛИ копирует немного, если он существует в любом из операндов. (A | B) даст 61, что составляет 0011 1101
^ Двоичный оператор XOR копирует бит, если он установлен в одном операнде, но не в обоих. (A ^ B) даст 49, что составляет 0011 0001
~ Оператор дополнения двоичных единиц является унарным и имеет эффект «переворачивания» битов. (~ A) даст -61, что составляет 1100 0011 в форме дополнения 2.
<< Двоичный оператор левого сдвига. Значение левого операнда перемещается влево на количество битов, указанное правым операндом. А << 2 даст 240, что составляет 1111 0000
>> Оператор двоичного правого сдвига. Значение левого операнда перемещается вправо на количество битов, указанное правым операндом. A >> 2 даст 15, что составляет 0000 1111

Операторы присваивания

Существуют следующие операторы присваивания, поддерживаемые языком Objective-C –

Показать примеры

оператор Описание пример
знак равно Простой оператор присваивания, присваивает значения от правых операндов к левому операнду C = A + B назначит значение A + B в C
+ = Добавить оператор присваивания И добавляет правый операнд к левому операнду и присваивает результат левому операнду C + = A эквивалентно C = C + A
знак равно Вычитание И оператор присваивания, вычитает правый операнд из левого операнда и присваивает результат левому операнду C – = A эквивалентно C = C – A
знак равно Оператор присваивания умножения И, умножает правый операнд на левый операнд и присваивает результат левому операнду C * = A эквивалентно C = C * A
знак равно Оператор деления И присваивания, делит левый операнд на правый операнд и присваивает результат левому операнду C / = A эквивалентно C = C / A
знак равно Модуль и оператор присваивания, принимает модуль с использованием двух операндов и присваивает результат левому операнду C% = A эквивалентно C = C% A
<< = Левый сдвиг И оператор присваивания C << = 2 совпадает с C = C << 2
>> = Оператор правого сдвига И присваивания C >> = 2 – это то же самое, что C = C >> 2
знак равно Побитовое И оператор присваивания C & = 2 совпадает с C = C & 2
^ = побитовое исключающее ИЛИ и оператор присваивания C ^ = 2 совпадает с C = C ^ 2
| = побитовое ИЛИ и оператор присваивания C | = 2 – это то же самое, что C = C | 2

Разные операторы ↦ sizeof & ternary

Есть несколько других важных операторов, включая sizeof и ? : поддерживается языком Objective-C.

Показать примеры

оператор Описание пример
размер() Возвращает размер переменной. sizeof (a), где a является целым числом, вернет 4.
& Возвращает адрес переменной. & А; даст фактический адрес переменной.
* Указатель на переменную. * А; будет указатель на переменную.
? : Условное выражение Если условие верно? Тогда значение X: в противном случае значение Y

Приоритет операторов в Objective-C

Приоритет оператора определяет группировку терминов в выражении. Это влияет на то, как оценивается выражение. Некоторые операторы имеют более высокий приоритет, чем другие; например, оператор умножения имеет более высокий приоритет, чем оператор сложения –

Например, х = 7 + 3 * 2; здесь x назначено 13, а не 20, потому что оператор * имеет более высокий приоритет, чем +, поэтому он сначала умножается на 3 * 2, а затем прибавляется к 7.

Здесь операторы с самым высоким приоритетом отображаются вверху таблицы, а операторы с самым низким – внизу. Внутри выражения операторы с более высоким приоритетом будут оцениваться первыми.

категория оператор Ассоциативность
постфикс () [] ->. ++ – – Слева направо
Одинарный + -! ~ ++ – – (тип) * & sizeof Справа налево
Multiplicative * /% Слева направо
присадка + – Слева направо
сдвиг << >> Слева направо
реляционный <<=>> = Слева направо
равенство ==! = Слева направо
Побитовый XOR ^ Слева направо
Побитовое ИЛИ | Слева направо
Логическое И && Слева направо
Логическое ИЛИ || Слева направо
условный ?: Справа налево
присваивание = + = – = * = / =% = >> = << = & = ^ = | = Справа налево
запятая , Слева направо

Циклы Objective-C

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

Языки программирования предоставляют различные управляющие структуры, которые допускают более сложные пути выполнения.

Оператор цикла позволяет нам выполнять оператор или группу операторов несколько раз, и в большинстве языков программирования ниже приводится общая форма инструкции цикла.

Петлевая архитектура

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

Sr.No. Тип и описание петли
1 в то время как цикл

Повторяет оператор или группу операторов, пока данное условие выполняется. Он проверяет условие перед выполнением тела цикла.

2 для цикла

Выполнить последовательность операторов несколько раз и сократить код, который управляет переменной цикла.

3 делать … пока цикл

Как оператор while, за исключением того, что он проверяет условие в конце тела цикла.

4 вложенные циклы

Вы можете использовать один или несколько циклов внутри любого другого цикла while, for или do.. while.

Повторяет оператор или группу операторов, пока данное условие выполняется. Он проверяет условие перед выполнением тела цикла.

Выполнить последовательность операторов несколько раз и сократить код, который управляет переменной цикла.

Как оператор while, за исключением того, что он проверяет условие в конце тела цикла.

Вы можете использовать один или несколько циклов внутри любого другого цикла while, for или do.. while.

Заявления о контроле цикла

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

Objective-C поддерживает следующие управляющие операторы. Нажмите на следующие ссылки, чтобы проверить их детали.

Sr.No. Контрольное заявление и описание
1 заявление о нарушении

Завершает оператор цикла или переключателя и передает выполнение в оператор, следующий сразу за циклом или переключателем.

2 продолжить заявление

Заставляет петлю пропускать оставшуюся часть своего тела и немедленно проверять свое состояние перед повторением.

Завершает оператор цикла или переключателя и передает выполнение в оператор, следующий сразу за циклом или переключателем.

Заставляет петлю пропускать оставшуюся часть своего тела и немедленно проверять свое состояние перед повторением.

Бесконечный цикл

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

#import <Foundation/Foundation.h>
 
int main () {

   for( ; ; ) {
      NSLog(@"This loop will run forever.\n");
   }

   return 0;
}

Когда условное выражение отсутствует, оно считается истинным. У вас может быть выражение инициализации и приращения, но программисты Objective-C чаще используют конструкцию for (;;) для обозначения бесконечного цикла.

Objective-C принятия решений

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

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

Заявления о принятии решений в Objective-C

Язык программирования Objective C принимает любые ненулевые и ненулевые значения как истинные , и если это или ноль или нуль , то это предполагается как ложное значение.

Язык программирования Objective-C предоставляет следующие типы операторов принятия решений. Нажмите на следующие ссылки, чтобы проверить их детали –

Sr.No. Заявление и описание
1 если заявление

Оператор if состоит из логического выражения, за которым следует одно или несколько операторов.

2 если … еще заявление

За оператором if может следовать необязательный оператор else , который выполняется, когда логическое выражение имеет значение false.

3 вложенные операторы if

Вы можете использовать один оператор if или else if внутри другого оператора if или else if .

4 заявление о переключении

Оператор switch позволяет проверять переменную на соответствие списку значений.

5 вложенные операторы switch

Вы можете использовать один оператор switch внутри другого оператора (ов) switch .

Оператор if состоит из логического выражения, за которым следует одно или несколько операторов.

За оператором if может следовать необязательный оператор else , который выполняется, когда логическое выражение имеет значение false.

Вы можете использовать один оператор if или else if внутри другого оператора if или else if .

Оператор switch позволяет проверять переменную на соответствие списку значений.

Вы можете использовать один оператор switch внутри другого оператора (ов) switch .

? : Оператор

Мы накрыли условного оператора? : в предыдущей главе, которая может быть использована для замены операторов if … else . Он имеет следующую общую форму –

Exp1 ? Exp2 : Exp3;

Где Exp1, Exp2 и Exp3 являются выражениями. Обратите внимание на использование и размещение толстой кишки.

Значение? Выражение определяется следующим образом: Exp1 оценивается. Если это правда, то Exp2 оценивается и становится значением целого? выражение. Если Exp1 имеет значение false, то Exp3 оценивается, и его значение становится значением выражения.

Функции Objective-C

Функция – это группа операторов, которые вместе выполняют задачу. Каждая программа Objective-C имеет одну функцию C, которая является main () , и все самые тривиальные программы могут определять дополнительные функции.

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

Объявление функции сообщает компилятору об имени функции, типе возврата и параметрах. Определение функции обеспечивает фактическое тело функции.

В основном в Objective-C мы называем функцию как метод.

Основа Objective-C предоставляет множество встроенных методов, которые может вызывать ваша программа. Например, метод appendString () для добавления строки в другую строку.

Метод известен с различными именами, такими как функция или подпрограмма или процедура и т. Д.

Определение метода

Общая форма определения метода в языке программирования Objective C следующая:

- (return_type) method_name🙁 argumentType1 )argumentName1 
joiningArgument2🙁 argumentType2 )argumentName2 ... 
joiningArgumentn🙁 argumentTypen )argumentNamen {
   body of the function
}

Определение метода в языке программирования Objective C состоит из заголовка метода и тела метода . Вот все части метода –

  • Тип возврата – метод может возвращать значение. Return_type – это тип данных значения, которое возвращает функция. Некоторые методы выполняют желаемые операции без возврата значения. В этом случае return_type является ключевым словом void .

  • Имя метода – это фактическое имя метода. Имя метода и список параметров вместе составляют сигнатуру метода.

  • Аргументы – Аргумент похож на заполнитель. Когда вызывается функция, вы передаете значение аргументу. Это значение называется фактическим параметром или аргументом. Список параметров относится к типу, порядку и количеству аргументов метода. Аргументы необязательны; то есть метод может не содержать аргументов.

  • Аргумент присоединения – объединяющий аргумент состоит в том, чтобы облегчить чтение и сделать его понятным во время вызова.

  • Тело методаТело метода содержит коллекцию операторов, которые определяют, что делает метод.

Тип возврата – метод может возвращать значение. Return_type – это тип данных значения, которое возвращает функция. Некоторые методы выполняют желаемые операции без возврата значения. В этом случае return_type является ключевым словом void .

Имя метода – это фактическое имя метода. Имя метода и список параметров вместе составляют сигнатуру метода.

Аргументы – Аргумент похож на заполнитель. Когда вызывается функция, вы передаете значение аргументу. Это значение называется фактическим параметром или аргументом. Список параметров относится к типу, порядку и количеству аргументов метода. Аргументы необязательны; то есть метод может не содержать аргументов.

Аргумент присоединения – объединяющий аргумент состоит в том, чтобы облегчить чтение и сделать его понятным во время вызова.

Тело методаТело метода содержит коллекцию операторов, которые определяют, что делает метод.

пример

Ниже приведен исходный код метода max () . Этот метод принимает два параметра num1 и num2 и возвращает максимум между двумя –

/* function returning the max between two numbers */
- (int) max🙁int) num1 secondNumber🙁int) num2 {
   
   /* local variable declaration */
   int result;
 
   if (num1 > num2) {
      result = num1;
   } else {
      result = num2;
   }
 
   return result; 
}

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

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

Объявление метода состоит из следующих частей:

- (return_type) function_name🙁 argumentType1 )argumentName1 
joiningArgument2🙁 argumentType2 )argumentName2 ... 
joiningArgumentn🙁 argumentTypen )argumentNamen;

Для определенной выше функции max () ниже приводится объявление метода –

-(int) max🙁int)num1 andNum2🙁int)num2;

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

Вызов метода

При создании метода Objective-C вы даете определение того, что должна делать функция. Чтобы использовать метод, вам нужно вызвать эту функцию для выполнения определенной задачи.

Когда программа вызывает функцию, управление программой передается вызываемому методу. Вызываемый метод выполняет определенную задачу, и когда выполняется его оператор return или когда достигается закрывающая скобка завершения функции, он возвращает управление программой обратно в основную программу.

Чтобы вызвать метод, вам просто нужно передать необходимые параметры вместе с именем метода, и если метод возвращает значение, вы можете сохранить возвращаемое значение. Например –

Live Demo

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
/* method declaration */
- (int)max🙁int)num1 andNum2🙁int)num2;
@end

@implementation SampleClass

/* method returning the max between two numbers */
- (int)max🙁int)num1 andNum2🙁int)num2 {

   /* local variable declaration */
   int result;
 
   if (num1 > num2) {
      result = num1;
   } else {
      result = num2;
   }
 
   return result; 
}

@end

int main () {
   
   /* local variable definition */
   int a = 100;
   int b = 200;
   int ret;
   
   SampleClass *sampleClass = [[SampleClass alloc]init];

   /* calling a method to get max value */
   ret = [sampleClass max:a andNum2:b];
 
   NSLog(@"Max value is : %d\n", ret );
   return 0;
}

Я сохранил функцию max () вместе с функцией main () и выполнил исходный код. Запустив финальный исполняемый файл, он даст следующий результат:

2013-09-07 22:28:45.912 demo[26080] Max value is : 200

Аргументы функции

Если функция должна использовать аргументы, она должна объявлять переменные, которые принимают значения аргументов. Эти переменные называются формальными параметрами функции.

Формальные параметры ведут себя как другие локальные переменные внутри функции и создаются при входе в функцию и уничтожаются при выходе.

При вызове функции существует два способа передачи аргументов в функцию:

Sr.No. Тип звонка и описание
1 Звонок по значению

Этот метод копирует фактическое значение аргумента в формальный параметр функции. В этом случае изменения, внесенные в параметр внутри функции, не влияют на аргумент.

2 Звоните по ссылке

Этот метод копирует адрес аргумента в формальный параметр. Внутри функции адрес используется для доступа к фактическому аргументу, используемому в вызове. Это означает, что изменения, внесенные в параметр, влияют на аргумент.

Этот метод копирует фактическое значение аргумента в формальный параметр функции. В этом случае изменения, внесенные в параметр внутри функции, не влияют на аргумент.

Этот метод копирует адрес аргумента в формальный параметр. Внутри функции адрес используется для доступа к фактическому аргументу, используемому в вызове. Это означает, что изменения, внесенные в параметр, влияют на аргумент.

По умолчанию Objective-C использует вызов по значению для передачи аргументов. В целом это означает, что код внутри функции не может изменять аргументы, используемые для вызова функции, и в приведенном выше примере при вызове функции max () использовался тот же метод.

Блоки Objective-C

Класс Objective-C определяет объект, который объединяет данные со связанным поведением. Иногда имеет смысл представлять одну задачу или единицу поведения, а не набор методов.

Блоки – это функция уровня языка, добавленная в C, Objective-C и C ++, которая позволяет создавать отдельные сегменты кода, которые можно передавать в методы или функции, как если бы они были значениями. Блоки – это объекты Objective-C, что означает, что они могут быть добавлены в коллекции, такие как NSArray или NSDictionary. Они также могут захватывать значения из окружающей области, делая их похожими на замыкания или лямбды в других языках программирования.

Синтаксис объявления простого блока

returntype (^blockName)(argumentType);

Простая реализация блока

returntype (^blockName)(argumentType)= ^{
};

Вот простой пример

void (^simpleBlock)(void) = ^{
   NSLog(@"This is a block");
};

Мы можем вызвать блок, используя

simpleBlock();

Блоки принимают аргументы и возвращают значения

Блоки также могут принимать аргументы и возвращать значения так же, как методы и функции.

Вот простой пример для реализации и вызова блока с аргументами и возвращаемыми значениями.

double (^multiplyTwoValues)(double, double) = 
   ^(double firstValue, double secondValue) {
      return firstValue * secondValue;
   };

double result = multiplyTwoValues(2,4); 
NSLog(@"The result is %f", result);

Блоки с использованием определений типов

Вот простой пример использования typedef в блоке. Обратите внимание, что этот пример пока не работает на онлайн-компиляторе . Используйте XCode, чтобы запустить то же самое.

#import <Foundation/Foundation.h>

typedef void (^CompletionBlock)();
@interface SampleClass:NSObject
- (void)performActionWithCompletion🙁CompletionBlock)completionBlock;
@end

@implementation SampleClass

- (void)performActionWithCompletion🙁CompletionBlock)completionBlock {

   NSLog(@"Action Performed");
   completionBlock();
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass performActionWithCompletion:^{
      NSLog(@"Completion is called to intimate action is performed.");
   }];

   return 0;
}

Давайте скомпилируем и выполним его, он даст следующий результат –

2013-09-10 08:13:57.155 demo[284:303] Action Performed
2013-09-10 08:13:57.157 demo[284:303] Completion is called to intimate action is performed.

Блоки чаще используются в приложениях iOS и Mac OS X. Поэтому важнее понимать использование блоков.

Числа Objective-C

В языке программирования Objective-C, чтобы сохранить основные типы данных, такие как int, float, bool в виде объекта,

Objective-C предоставляет ряд методов для работы с NSNumber, и важные из них перечислены в следующей таблице.

Sr.No. Метод и описание
1

+ (NSNumber *) numberWithBool: (BOOL) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, обрабатывая его как BOOL.

2

+ (NSNumber *) numberWithChar: (char) значение

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

3

+ (NSNumber *) numberWithDouble: (double) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, рассматривая его как double.

4

+ (NSNumber *) numberWithFloat: (float) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, обрабатывая его как число с плавающей точкой.

5

+ (NSNumber *) numberWithInt: (int) значение

Создает и возвращает объект NSNumber, содержащий данное значение, рассматривая его как подписанное int.

6

+ (NSNumber *) numberWithInteger: (NSInteger) значение

Создает и возвращает объект NSNumber, содержащий данное значение, рассматривая его как NSInteger.

7

– (BOOL) boolValue

Возвращает значение получателя в виде BOOL.

8

– (char) charValue

Возвращает значение получателя в виде символа.

9

– (double) doubleValue

Возвращает значение получателя как double.

10

– (float) floatValue

Возвращает значение получателя в виде числа с плавающей запятой.

11

– (NSInteger) integerValue

Возвращает значение получателя как NSInteger.

12

– (int) intValue

Возвращает значение получателя как int.

13

– (NSString *) stringValue

Возвращает значение получателя в виде удобочитаемой строки.

+ (NSNumber *) numberWithBool: (BOOL) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, обрабатывая его как BOOL.

+ (NSNumber *) numberWithChar: (char) значение

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

+ (NSNumber *) numberWithDouble: (double) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, рассматривая его как double.

+ (NSNumber *) numberWithFloat: (float) значение

Создает и возвращает объект NSNumber, содержащий заданное значение, обрабатывая его как число с плавающей точкой.

+ (NSNumber *) numberWithInt: (int) значение

Создает и возвращает объект NSNumber, содержащий данное значение, рассматривая его как подписанное int.

+ (NSNumber *) numberWithInteger: (NSInteger) значение

Создает и возвращает объект NSNumber, содержащий данное значение, рассматривая его как NSInteger.

– (BOOL) boolValue

Возвращает значение получателя в виде BOOL.

– (char) charValue

Возвращает значение получателя в виде символа.

– (double) doubleValue

Возвращает значение получателя как double.

– (float) floatValue

Возвращает значение получателя в виде числа с плавающей запятой.

– (NSInteger) integerValue

Возвращает значение получателя как NSInteger.

– (int) intValue

Возвращает значение получателя как int.

– (NSString *) stringValue

Возвращает значение получателя в виде удобочитаемой строки.

Вот простой пример использования NSNumber, который умножает два числа и возвращает продукт.

Live Demo

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (NSNumber *)multiplyA🙁NSNumber *)a withB🙁NSNumber *)b;
@end

@implementation SampleClass

- (NSNumber *)multiplyA🙁NSNumber *)a withB🙁NSNumber *)b {
   float number1 = [a floatValue];
   float number2 = [b floatValue];
   float product = number1 * number2;
   NSNumber *result = [NSNumber numberWithFloat:product];
   return result;
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

   SampleClass *sampleClass = [[SampleClass alloc]init];
   NSNumber *a = [NSNumber numberWithFloat:10.5];
   NSNumber *b = [NSNumber numberWithFloat:10.0];   
   NSNumber *result = [sampleClass multiplyA:a withB:b];
   NSString *resultString = [result stringValue];
   NSLog(@"The product is %@",resultString);

   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-14 18:53:40.575 demo[16787] The product is 105

Массивы Objective-C

Язык программирования Objective-C предоставляет структуру данных, называемую массивом , в которой можно хранить последовательную коллекцию элементов одного и того же типа. Массив используется для хранения коллекции данных, но часто более полезно думать о массиве как о коллекции переменных одного типа.

Вместо того, чтобы объявлять отдельные переменные, такие как number0, number1, … и number99, вы объявляете одну переменную массива, такую ​​как числа, и используете числа [0], числа [1] и …, числа [99] для представления отдельные переменные. Определенный элемент в массиве доступен по индексу.

Все массивы состоят из смежных областей памяти. Самый низкий адрес соответствует первому элементу, а самый высокий адрес – последнему.

Массивы в Objective-C

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

Чтобы объявить массив в Objective-C, программист определяет тип элементов и количество элементов, требуемых массивом, следующим образом:

type arrayName [ arraySize ];

Это называется одномерным массивом. ArraySize должен быть целочисленной константой, большей нуля, и тип может быть любым допустимым типом данных Objective C. Например, чтобы объявить массив из 10 элементов с именем balance типа double, используйте этот оператор –

double balance[10];

Теперь баланс – это переменный массив, которого достаточно для хранения до 10 двойных чисел.

Инициализация массивов

Вы можете инициализировать массив в Objective-C либо один за другим, либо используя один оператор следующим образом:

double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};

Число значений в фигурных скобках {} не может быть больше, чем количество элементов, которые мы объявляем для массива в квадратных скобках []. Ниже приведен пример назначения одного элемента массива:

Если вы опустите размер массива, будет создан массив, достаточно большой, чтобы вместить инициализацию. Поэтому, если вы напишите –

double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0};

Вы создадите точно такой же массив, как и в предыдущем примере.

balance[4] = 50.0;

Приведенный выше оператор присваивает элементу номер 5 в массиве значение 50,0. Массив с 4-м индексом будет 5-м, т. Е. Последним элементом, поскольку все массивы имеют 0 в качестве индекса их первого элемента, который также называется базовым индексом. Ниже приведено графическое представление того же массива, который мы обсуждали выше.

Презентация массива

Доступ к элементам массива

Доступ к элементу осуществляется путем индексации имени массива. Это делается путем помещения индекса элемента в квадратные скобки после имени массива. Например –

double salary = balance[9];

Приведенный выше оператор возьмет 10-й элемент из массива и присвоит значение переменной salary. Ниже приведен пример, который будет использовать все три вышеупомянутых понятия, а именно. декларация, назначение и доступ к массивам –

Live Demo

#import <Foundation/Foundation.h>
 
int main () {
   int n[ 10 ];   /* n is an array of 10 integers */
   int i,j;
 
   /* initialize elements of array n to 0 */         
   for ( i = 0; i < 10; i++ ) {
      n[ i ] = i + 100;    /* set element at location i to i + 100 */
   }
   
   /* output each array element's value */
   for (j = 0; j < 10; j++ ) {
      NSLog(@"Element[%d] = %d\n", j, n[j] );
   }
 
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 01:24:06.669 demo[16508] Element[0] = 100
2013-09-14 01:24:06.669 demo[16508] Element[1] = 101
2013-09-14 01:24:06.669 demo[16508] Element[2] = 102
2013-09-14 01:24:06.669 demo[16508] Element[3] = 103
2013-09-14 01:24:06.669 demo[16508] Element[4] = 104
2013-09-14 01:24:06.669 demo[16508] Element[5] = 105
2013-09-14 01:24:06.669 demo[16508] Element[6] = 106
2013-09-14 01:24:06.669 demo[16508] Element[7] = 107
2013-09-14 01:24:06.669 demo[16508] Element[8] = 108
2013-09-14 01:24:06.669 demo[16508] Element[9] = 109

Массивы Objective-C в деталях

Массивы важны для Objective-C и требуют много дополнительных деталей. Существует несколько важных понятий, связанных с массивом, которые должны быть понятны программисту Objective-C –

Sr.No. Концепция и описание
1 Многомерные массивы

Objective-C поддерживает многомерные массивы. Простейшей формой многомерного массива является двумерный массив.

2 Передача массивов в функции

Вы можете передать функции указатель на массив, указав имя массива без индекса.

3 Возврат массива из функции

Objective-C позволяет функции возвращать массив.

4 Указатель на массив

Вы можете сгенерировать указатель на первый элемент массива, просто указав имя массива без индекса.

Objective-C поддерживает многомерные массивы. Простейшей формой многомерного массива является двумерный массив.

Вы можете передать функции указатель на массив, указав имя массива без индекса.

Objective-C позволяет функции возвращать массив.

Вы можете сгенерировать указатель на первый элемент массива, просто указав имя массива без индекса.

Objective-C Указатели

Указатели в Objective-C легко и весело выучить. Некоторые задачи программирования в Objective-C легче выполнять с помощью указателей, а другие задачи, такие как динамическое распределение памяти, невозможно выполнить без использования указателей. Поэтому становится необходимым изучать указатели, чтобы стать идеальным программистом на Objective-C. Давайте начнем изучать их в простых и легких шагах.

Как вы знаете, каждая переменная является ячейкой памяти, и каждая ячейка памяти имеет свой адрес, к которому можно обратиться, используя оператор амперсанда (&), который обозначает адрес в памяти. Рассмотрим следующий пример, который выведет адрес определенных переменных:

Live Demo

#import <Foundation/Foundation.h>

int main () {
   int  var1;
   char var2[10];

   NSLog(@"Address of var1 variable: %x\n", &var1 );
   NSLog(@"Address of var2 variable: %x\n", &var2 );

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он выдает результат примерно так:

2013-09-13 03:18:45.727 demo[17552] Address of var1 variable: 1c0843fc
2013-09-13 03:18:45.728 demo[17552] Address of var2 variable: 1c0843f0

Итак, вы поняли, что такое адрес памяти и как к нему обращаться, так что основа концепции окончена. Теперь давайте посмотрим, что такое указатель.

Что такое указатели?

Указатель – это переменная, значением которой является адрес другой переменной, т. Е. Прямой адрес ячейки памяти. Как и любая переменная или константа, вы должны объявить указатель, прежде чем использовать его для хранения любого адреса переменной. Общая форма объявления переменной указателя –

type *var-name;

Здесь тип – это базовый тип указателя; это должен быть допустимый тип данных Objective C, а var-name – это имя переменной указателя. Звездочка *, которую вы использовали для объявления указателя, та же, что и для умножения. Однако в этом утверждении звездочка используется для обозначения переменной в качестве указателя. Следующее является действительным объявлением указателя –

int    *ip;    /* pointer to an integer */
double *dp;    /* pointer to a double */
float  *fp;    /* pointer to a float */
char   *ch     /* pointer to a character */

Фактический тип данных значения всех указателей, будь то целое число, число с плавающей запятой, символ или другое, является одним и тем же, длинное шестнадцатеричное число, представляющее адрес памяти. Единственное различие между указателями разных типов данных – это тип данных переменной или константы, на которую указывает указатель.

Как использовать указатели?

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

Live Demo

#import <Foundation/Foundation.h>

int main () {
   int  var = 20;    /* actual variable declaration */
   int  *ip;         /* pointer variable declaration */  
   ip = &var;       /* store address of var in pointer variable*/

   NSLog(@"Address of var variable: %x\n", &var  );

   /* address stored in pointer variable */
   NSLog(@"Address stored in ip variable: %x\n", ip );

   /* access the value using the pointer */
   NSLog(@"Value of *ip variable: %d\n", *ip );

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он выдает результат примерно так:

2013-09-13 03:20:21.873 demo[24179] Address of var variable: 337ed41c
2013-09-13 03:20:21.873 demo[24179] Address stored in ip variable: 337ed41c
2013-09-13 03:20:21.874 demo[24179] Value of *ip variable: 20

NULL-указатели в Objective-C

Хорошей практикой всегда является присвоение значения NULL переменной-указателю, если у вас нет точного адреса для назначения. Это делается во время объявления переменной. Указатель, которому присвоен NULL, называется нулевым указателем.

Указатель NULL – это константа со значением ноль, определенная в нескольких стандартных библиотеках. Рассмотрим следующую программу –

Live Demo

#import <Foundation/Foundation.h>

int main () {
   int  *ptr = NULL;
   NSLog(@"The value of ptr is : %x\n", ptr  );
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-13 03:21:19.447 demo[28027] The value of ptr is : 0

В большинстве операционных систем программам не разрешен доступ к памяти по адресу 0, поскольку эта память зарезервирована операционной системой. Однако адрес памяти 0 имеет особое значение; он сигнализирует о том, что указатель не предназначен для указания на доступную ячейку памяти. Но по соглашению, если указатель содержит нулевое (нулевое) значение, предполагается, что он ничего не указывает.

Чтобы проверить нулевой указатель, вы можете использовать оператор if следующим образом:

if(ptr)     /* succeeds if p is not null */
if(!ptr)    /* succeeds if p is null */

Objective-C Указатели в деталях

У указателей есть много, но простых концепций, и они очень важны для программирования на Objective-C. Существует несколько важных концепций указателей, которые должны быть понятны программисту Objective-C:

Sr.No. Концепция и описание
1 Objective-C – арифметика указателя

Существует четыре арифметических оператора, которые можно использовать в указателях: ++, -, +, –

2 Objective-C – Массив указателей

Вы можете определить массивы для хранения нескольких указателей.

3 Objective-C – указатель на указатель

Objective-C позволяет вам иметь указатель на указатель и так далее.

4 Передача указателей на функции в Objective-C

Передача аргумента по ссылке или по адресу позволяет включить переданный аргумент в вызывающей функции вызываемой функцией.

5 Возврат указателя из функций в Objective-C

Objective-C позволяет функции возвращать указатель на локальную переменную, статическую переменную и динамически распределенную память.

Существует четыре арифметических оператора, которые можно использовать в указателях: ++, -, +, –

Вы можете определить массивы для хранения нескольких указателей.

Objective-C позволяет вам иметь указатель на указатель и так далее.

Передача аргумента по ссылке или по адресу позволяет включить переданный аргумент в вызывающей функции вызываемой функцией.

Objective-C позволяет функции возвращать указатель на локальную переменную, статическую переменную и динамически распределенную память.

Строки Objective-C

Строка в языке программирования Objective-C представляется с использованием NSString, а ее подкласс NSMutableString предоставляет несколько способов создания строковых объектов. Самый простой способ создать строковый объект – использовать конструкцию Objective-C @ “…” –

NSString *greeting = @"Hello";

Простой пример создания и печати строки показан ниже.

Live Demo

#import <Foundation/Foundation.h>

int main () {
   NSString *greeting = @"Hello";
   NSLog(@"Greeting message: %@\n", greeting );

   return 0;
}

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

2013-09-11 01:21:39.922 demo[23926] Greeting message: Hello

Objective-C поддерживает широкий спектр методов для работы со строками –

Sr.No. Метод и цель
1

– (NSString *) capitalizedString;

Возвращает заглавное представление получателя.

2

– (unichar) characterAtIndex: (NSUInteger) индекс;

Возвращает символ в заданной позиции массива.

3

– (double) doubleValue;

Возвращает значение с плавающей запятой текста получателя как double.

4

– (float) floatValue;

Возвращает значение с плавающей запятой текста получателя в виде числа с плавающей запятой.

5

– (BOOL) hasPrefix: (NSString *) aString;

Возвращает логическое значение, которое указывает, соответствует ли данная строка начальным символам получателя.

6

– (BOOL) имеет суффикс: (NSString *) aString;

Возвращает логическое значение, которое указывает, соответствует ли данная строка конечным символам получателя.

7

– (id) initWithFormat: (NSString *) формат …;

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

8

– (NSInteger) integerValue;

Возвращает значение NSInteger текста получателя.

9

– (BOOL) isEqualToString: (NSString *) aString;

Возвращает логическое значение, которое указывает, равна ли данная строка получателю, используя буквальное сравнение на основе Unicode.

10

– (NSUInteger) длина;

Возвращает количество символов Unicode в получателе.

11

– (NSString *) lowercaseString;

Возвращает представление получателя в нижнем регистре.

12

– (NSRange) rangeOfString: (NSString *) aString;

Находит и возвращает диапазон первого вхождения данной строки в получателе.

13

– (NSString *) stringByAppendingFormat: (NSString *) формат …;

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

14

– (NSString *) stringByTrimmingCharactersInSet: (NSCharacterSet *) set;

Возвращает новую строку, созданную удалением с обоих концов символов получателя, содержащихся в данном наборе символов.

15

– (NSString *) substringFromIndex: (NSUInteger) anIndex;

Возвращает новую строку, содержащую символы получателя, от строки с заданным индексом до конца.

– (NSString *) capitalizedString;

Возвращает заглавное представление получателя.

– (unichar) characterAtIndex: (NSUInteger) индекс;

Возвращает символ в заданной позиции массива.

– (double) doubleValue;

Возвращает значение с плавающей запятой текста получателя как double.

– (float) floatValue;

Возвращает значение с плавающей запятой текста получателя в виде числа с плавающей запятой.

– (BOOL) hasPrefix: (NSString *) aString;

Возвращает логическое значение, которое указывает, соответствует ли данная строка начальным символам получателя.

– (BOOL) имеет суффикс: (NSString *) aString;

Возвращает логическое значение, которое указывает, соответствует ли данная строка конечным символам получателя.

– (id) initWithFormat: (NSString *) формат …;

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

– (NSInteger) integerValue;

Возвращает значение NSInteger текста получателя.

– (BOOL) isEqualToString: (NSString *) aString;

Возвращает логическое значение, которое указывает, равна ли данная строка получателю, используя буквальное сравнение на основе Unicode.

– (NSUInteger) длина;

Возвращает количество символов Unicode в получателе.

– (NSString *) lowercaseString;

Возвращает представление получателя в нижнем регистре.

– (NSRange) rangeOfString: (NSString *) aString;

Находит и возвращает диапазон первого вхождения данной строки в получателе.

– (NSString *) stringByAppendingFormat: (NSString *) формат …;

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

– (NSString *) stringByTrimmingCharactersInSet: (NSCharacterSet *) set;

Возвращает новую строку, созданную удалением с обоих концов символов получателя, содержащихся в данном наборе символов.

– (NSString *) substringFromIndex: (NSUInteger) anIndex;

Возвращает новую строку, содержащую символы получателя, от строки с заданным индексом до конца.

Следующий пример использует несколько из вышеупомянутых функций –

Live Demo

#import <Foundation/Foundation.h>

int main () {
   NSString *str1 = @"Hello";
   NSString *str2 = @"World";
   NSString *str3;
   int  len ;

   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

   /* uppercase string */
   str3 = [str2 uppercaseString];
   NSLog(@"Uppercase String :  %@\n", str3 );

   /* concatenates str1 and str2 */
   str3 = [str1 stringByAppendingFormat:@"World"];
   NSLog(@"Concatenated string:   %@\n", str3 );

   /* total length of str3 after concatenation */
   len = [str3 length];
   NSLog(@"Length of Str3 :  %d\n", len );

   /* InitWithFormat */
   str3 = [[NSString alloc] initWithFormat:@"%@ %@",str1,str2];	
   NSLog(@"Using initWithFormat:   %@\n", str3 );
   [pool drain];

   return 0;
}

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

2013-09-11 01:15:45.069 demo[30378] Uppercase String :  WORLD
2013-09-11 01:15:45.070 demo[30378] Concatenated string:   HelloWorld
2013-09-11 01:15:45.070 demo[30378] Length of Str3 :  10
2013-09-11 01:15:45.070 demo[30378] Using initWithFormat:   Hello World

Вы можете найти полный список связанных с NSString методов Objective-C в Справочнике по классам NSString.

Objective-C Структуры

Массивы Objective C позволяют вам определять тип переменных, которые могут содержать несколько элементов данных одного типа, но структура – это еще один определенный пользователем тип данных, доступный в программировании Objective C, который позволяет комбинировать элементы данных разных видов.

Структуры используются для представления записей. Предположим, вы хотите отслеживать свои книги в библиотеке. Вы можете отслеживать следующие атрибуты о каждой книге –

  • заглавие
  • автор
  • Предмет
  • ID книги

Определение структуры

Чтобы определить структуру, вы должны использовать инструкцию struct . Оператор struct определяет новый тип данных с более чем одним членом для вашей программы. Формат инструкции struct такой:

struct [structure tag] {
   member definition;
   member definition;
   ...
   member definition;
} [one or more structure variables];  

Тег структуры является необязательным, и каждое определение члена является обычным определением переменной, например int i; или плавать f; или любое другое допустимое определение переменной. В конце определения структуры перед последней точкой с запятой вы можете указать одну или несколько переменных структуры, но это не обязательно. Вот как вы бы объявили структуру Книги –

struct Books {
   NSString *title;
   NSString *author;
   NSString *subject;
   int   book_id;
} book;  

Доступ к членам структуры

Чтобы получить доступ к любому члену структуры, мы используем оператор доступа к члену (.) . Оператор доступа к элементу кодируется как точка между именем структурной переменной и элементом структуры, к которому мы хотим получить доступ. Вы должны использовать ключевое слово struct для определения переменных типа структуры. Ниже приведен пример, объясняющий использование структуры:

Live Demo

#import <Foundation/Foundation.h>

struct Books {
   NSString *title;
   NSString *author;
   NSString *subject;
   int   book_id;
};
 
int main() {
   struct Books Book1;        /* Declare Book1 of type Book */
   struct Books Book2;        /* Declare Book2 of type Book */
 
   /* book 1 specification */
   Book1.title = @"Objective-C Programming";
   Book1.author = @"Nuha Ali"; 
   Book1.subject = @"Objective-C Programming Tutorial";
   Book1.book_id = 6495407;

   /* book 2 specification */
   Book2.title = @"Telecom Billing";
   Book2.author = @"Zara Ali";
   Book2.subject = @"Telecom Billing Tutorial";
   Book2.book_id = 6495700;
 
   /* print Book1 info */
   NSLog(@"Book 1 title : %@\n", Book1.title);
   NSLog(@"Book 1 author : %@\n", Book1.author);
   NSLog(@"Book 1 subject : %@\n", Book1.subject);
   NSLog(@"Book 1 book_id : %d\n", Book1.book_id);

   /* print Book2 info */
   NSLog(@"Book 2 title : %@\n", Book2.title);
   NSLog(@"Book 2 author : %@\n", Book2.author);
   NSLog(@"Book 2 subject : %@\n", Book2.subject);
   NSLog(@"Book 2 book_id : %d\n", Book2.book_id);

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 04:20:07.947 demo[20591] Book 1 title : Objective-C Programming
2013-09-14 04:20:07.947 demo[20591] Book 1 author : Nuha Ali
2013-09-14 04:20:07.947 demo[20591] Book 1 subject : Objective-C Programming Tutorial
2013-09-14 04:20:07.947 demo[20591] Book 1 book_id : 6495407
2013-09-14 04:20:07.947 demo[20591] Book 2 title : Telecom Billing
2013-09-14 04:20:07.947 demo[20591] Book 2 author : Zara Ali
2013-09-14 04:20:07.947 demo[20591] Book 2 subject : Telecom Billing Tutorial
2013-09-14 04:20:07.947 demo[20591] Book 2 book_id : 6495700

Структуры как аргументы функций

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

Live Demo

#import <Foundation/Foundation.h>

struct Books {
   NSString *title;
   NSString *author;
   NSString *subject;
   int   book_id;
};

@interface SampleClass:NSObject
/* function declaration */
- (void) printBook🙁 struct Books) book ;
@end

@implementation SampleClass 

- (void) printBook🙁 struct Books) book {
   NSLog(@"Book title : %@\n", book.title);
   NSLog(@"Book author : %@\n", book.author);
   NSLog(@"Book subject : %@\n", book.subject);
   NSLog(@"Book book_id : %d\n", book.book_id);
}

@end

int main() {
   struct Books Book1;        /* Declare Book1 of type Book */
   struct Books Book2;        /* Declare Book2 of type Book */
 
   /* book 1 specification */
   Book1.title = @"Objective-C Programming";
   Book1.author = @"Nuha Ali"; 
   Book1.subject = @"Objective-C Programming Tutorial";
   Book1.book_id = 6495407;

   /* book 2 specification */
   Book2.title = @"Telecom Billing";
   Book2.author = @"Zara Ali";
   Book2.subject = @"Telecom Billing Tutorial";
   Book2.book_id = 6495700;
 
   SampleClass *sampleClass = [[SampleClass alloc]init];
   /* print Book1 info */
   [sampleClass printBook: Book1];

   /* Print Book2 info */
   [sampleClass printBook: Book2];

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 04:34:45.725 demo[8060] Book title : Objective-C Programming
2013-09-14 04:34:45.725 demo[8060] Book author : Nuha Ali
2013-09-14 04:34:45.725 demo[8060] Book subject : Objective-C Programming Tutorial
2013-09-14 04:34:45.725 demo[8060] Book book_id : 6495407
2013-09-14 04:34:45.725 demo[8060] Book title : Telecom Billing
2013-09-14 04:34:45.725 demo[8060] Book author : Zara Ali
2013-09-14 04:34:45.725 demo[8060] Book subject : Telecom Billing Tutorial
2013-09-14 04:34:45.725 demo[8060] Book book_id : 6495700

Указатели на структуры

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

struct Books *struct_pointer;

Теперь вы можете сохранить адрес структурной переменной в указанной выше переменной-указателе. Чтобы найти адрес структурной переменной, поместите оператор & перед именем структуры следующим образом:

struct_pointer = &Book1;

Чтобы получить доступ к членам структуры, используя указатель на эту структуру, вы должны использовать оператор -> следующим образом –

struct_pointer->title;

Давайте перепишем приведенный выше пример, используя указатель структуры, надеюсь, вам будет легко понять концепцию –

Live Demo

#import <Foundation/Foundation.h>

struct Books {
   NSString *title;
   NSString *author;
   NSString *subject;
   int   book_id;
};

@interface SampleClass:NSObject
/* function declaration */
- (void) printBook🙁 struct Books *) book ;
@end

@implementation SampleClass 
- (void) printBook🙁 struct Books *) book {
   NSLog(@"Book title : %@\n", book->title);
   NSLog(@"Book author : %@\n", book->author);
   NSLog(@"Book subject : %@\n", book->subject);
   NSLog(@"Book book_id : %d\n", book->book_id);
}

@end

int main() {
   struct Books Book1;        /* Declare Book1 of type Book */
   struct Books Book2;        /* Declare Book2 of type Book */
 
   /* book 1 specification */
   Book1.title = @"Objective-C Programming";
   Book1.author = @"Nuha Ali"; 
   Book1.subject = @"Objective-C Programming Tutorial";
   Book1.book_id = 6495407;

   /* book 2 specification */
   Book2.title = @"Telecom Billing";
   Book2.author = @"Zara Ali";
   Book2.subject = @"Telecom Billing Tutorial";
   Book2.book_id = 6495700;
 
   SampleClass *sampleClass = [[SampleClass alloc]init];
   /* print Book1 info by passing address of Book1 */
   [sampleClass printBook:&Book1];

   /* print Book2 info by passing address of Book2 */
   [sampleClass printBook:&Book2];

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 04:38:13.942 demo[20745] Book title : Objective-C Programming
2013-09-14 04:38:13.942 demo[20745] Book author : Nuha Ali
2013-09-14 04:38:13.942 demo[20745] Book subject : Objective-C Programming Tutorial
2013-09-14 04:38:13.942 demo[20745] Book book_id : 6495407
2013-09-14 04:38:13.942 demo[20745] Book title : Telecom Billing
2013-09-14 04:38:13.942 demo[20745] Book author : Zara Ali
2013-09-14 04:38:13.942 demo[20745] Book subject : Telecom Billing Tutorial
2013-09-14 04:38:13.942 demo[20745] Book book_id : 6495700

Битовые поля

Битовые поля позволяют упаковывать данные в структуру. Это особенно полезно, когда память или хранилище данных стоят дорого. Типичные примеры –

  • Упаковка нескольких объектов в машинное слово. например, 1-битные флаги могут быть сжаты.

  • Чтение внешних форматов файлов – могут быть прочитаны нестандартные форматы файлов. Например, 9-битные целые числа.

Упаковка нескольких объектов в машинное слово. например, 1-битные флаги могут быть сжаты.

Чтение внешних форматов файлов – могут быть прочитаны нестандартные форматы файлов. Например, 9-битные целые числа.

Objective-C позволяет нам делать это в определении структуры, помещая: bit length после переменной. Например –

struct packed_struct {
   unsigned int f1:1;
   unsigned int f2:1;
   unsigned int f3:1;
   unsigned int f4:1;
   unsigned int type:4;
   unsigned int my_int:9;
} pack;

Здесь pack_struct содержит 6 членов: четыре 1-битных флага f1..f3, 4-битный тип и 9-битный my_int.

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

Препроцессоры Objective-C

Препроцессор Objective-C не является частью компилятора, но является отдельным этапом в процессе компиляции. Проще говоря, препроцессор Objective-C – это всего лишь инструмент подстановки текста, который инструктирует компилятор выполнить необходимую предварительную обработку перед фактической компиляцией. Мы будем называть препроцессор Objective-C OCPP.

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

Sr.No. Директива и описание
1

#define

Заменяет макрос препроцессора

2

#включают

Вставляет определенный заголовок из другого файла

3

#undef

Определяет макрос препроцессора

4

#ifdef

Возвращает true, если этот макрос определен

5

#ifndef

Возвращает true, если этот макрос не определен

6

#если

Проверяет, верно ли условие времени компиляции

7

#else

Альтернатива для #if

8

#elif

# еще #if в одном утверждении

9

#endif

Завершает препроцессор условно

10

#ошибка

Распечатывает сообщение об ошибке на stderr

11

#pragma

Выдает специальные команды компилятору с использованием стандартизированного метода

#define

Заменяет макрос препроцессора

#включают

Вставляет определенный заголовок из другого файла

#undef

Определяет макрос препроцессора

#ifdef

Возвращает true, если этот макрос определен

#ifndef

Возвращает true, если этот макрос не определен

#если

Проверяет, верно ли условие времени компиляции

#else

Альтернатива для #if

#elif

# еще #if в одном утверждении

#endif

Завершает препроцессор условно

#ошибка

Распечатывает сообщение об ошибке на stderr

#pragma

Выдает специальные команды компилятору с использованием стандартизированного метода

Примеры препроцессоров

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

#define MAX_ARRAY_LENGTH 20

Эта директива указывает OCPP заменять экземпляры MAX_ARRAY_LENGTH на 20. Используйте #define для констант, чтобы повысить удобочитаемость.

#import <Foundation/Foundation.h>
#include "myheader.h"

Эти директивы сообщают OCPP, что нужно получить файл foundation.h из Foundation Framework и добавить текст в текущий исходный файл. Следующая строка говорит OCPP, чтобы получить myheader.h из локального каталога и добавить содержимое в текущий исходный файл.

#undef  FILE_SIZE
#define FILE_SIZE 42

Это говорит OCPP отменить определение существующего FILE_SIZE и определить его как 42.

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

Это говорит OCPP определять MESSAGE, только если MESSAGE еще не определен.

#ifdef DEBUG
   /* Your debugging statements here */
#endif

Это говорит OCPP, что нужно выполнить обработку вложенных операторов, если определен DEBUG. Это полезно, если вы передаете флаг -DDEBUG компилятору gcc во время компиляции. Это определит DEBUG, так что вы можете включать и выключать отладку на лету во время компиляции.

Предопределенные макросы

ANSI C определяет количество макросов. Хотя каждый из них доступен для использования в программировании, предопределенные макросы не должны изменяться напрямую.

Sr.No. Макрос и описание
1

__ДАТА__

Текущая дата в виде символьного литерала в формате “МММ ДД ГГГГ”

2

__ВРЕМЯ__

Текущее время как символьный литерал в формате “ЧЧ: ММ: СС”

3

__ФАЙЛ__

Это содержит текущее имя файла в виде строкового литерала.

4

__ЛИНИЯ__

Он содержит номер текущей строки в виде десятичной константы.

5

__STDC__

Определяется как 1, когда компилятор соответствует стандарту ANSI.

__ДАТА__

Текущая дата в виде символьного литерала в формате “МММ ДД ГГГГ”

__ВРЕМЯ__

Текущее время как символьный литерал в формате “ЧЧ: ММ: СС”

__ФАЙЛ__

Это содержит текущее имя файла в виде строкового литерала.

__ЛИНИЯ__

Он содержит номер текущей строки в виде десятичной константы.

__STDC__

Определяется как 1, когда компилятор соответствует стандарту ANSI.

Давайте попробуем следующий пример –

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSLog(@"File :%s\n", __FILE__ );
   NSLog(@"Date :%s\n", __DATE__ );
   NSLog(@"Time :%s\n", __TIME__ );
   NSLog(@"Line :%d\n", __LINE__ );
   NSLog(@"ANSI :%d\n", __STDC__ );
   
   return 0;
}

Когда приведенный выше код в файле main.m компилируется и выполняется, он дает следующий результат –

2013-09-14 04:46:14.859 demo[20683] File :main.m
2013-09-14 04:46:14.859 demo[20683] Date :Sep 14 2013
2013-09-14 04:46:14.859 demo[20683] Time :04:46:14
2013-09-14 04:46:14.859 demo[20683] Line :8
2013-09-14 04:46:14.859 demo[20683] ANSI :1

Операторы препроцессора

Препроцессор Objective-C предлагает следующие операторы, которые помогут вам в создании макросов:

Продолжение макроса (\)

Макрос обычно должен содержаться в одной строке. Оператор продолжения макроса используется для продолжения макроса, который слишком длинный для одной строки. Например –

#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")

Stringize (#)

Оператор stringize или number-sign (‘#’), когда используется в определении макроса, преобразует параметр макроса в строковую константу. Этот оператор может использоваться только в макросе, который имеет указанный аргумент или список параметров. Например –

Live Demo

#import <Foundation/Foundation.h>

#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")

int main(void) {
   message_for(Carole, Debra);
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 05:46:14.859 demo[20683] Carole and Debra: We love you!

Вставка токена (##)

Оператор вставки токена (##) в определении макроса объединяет два аргумента. Он позволяет объединить два отдельных токена в определении макроса в один токен. Например –

Live Demo

#import <Foundation/Foundation.h>

#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)

int main(void) {
   int token34 = 40;
   
   tokenpaster(34);
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 05:48:14.859 demo[20683] token34 = 40

Как это произошло, потому что этот пример приводит к следующему фактическому выводу препроцессора –

NSLog (@"token34 = %d", token34);

В этом примере показано объединение токена ## n в token34, и здесь мы использовали как stringize, так и вставку токена .

Определенный () оператор

Определяемый препроцессором оператор используется в константных выражениях, чтобы определить, определен ли идентификатор с помощью #define. Если указанный идентификатор определен, значение равно true (не ноль). Если символ не определен, значение равно false (ноль). Определенный оператор указан следующим образом:

Live Demo

#import <Foundation/Foundation.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void) {
   NSLog(@"Here is the message: %s\n", MESSAGE);  
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 05:48:19.859 demo[20683] Here is the message: You wish!

Параметризованные макросы

Одной из мощных функций OCPP является возможность имитировать функции с помощью параметризованных макросов. Например, у нас может быть некоторый код для возведения в квадрат числа следующим образом:

int square(int x) {
   return x * x;
}

Мы можем переписать приведенный выше код с помощью макроса следующим образом:

#define square(x) ((x) * (x))

Макросы с аргументами должны быть определены с использованием директивы #define, прежде чем их можно будет использовать. Список аргументов заключен в круглые скобки и должен следовать сразу за именем макроса. Пробелы между именем макроса и открытыми скобками не допускаются. Например –

Live Demo

#import <Foundation/Foundation.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
   NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));  
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 05:52:15.859 demo[20683] Max between 20 and 10 is 20

Objective-C Typedef

Язык программирования Objective-C предоставляет ключевое слово typedef , которое можно использовать для присвоения типу нового имени. Ниже приведен пример определения термина BYTE для однобайтовых чисел:

typedef unsigned char BYTE;

После определения этого типа идентификатор BYTE можно использовать как сокращение для типа unsigned char, например:.

BYTE  b1, b2;

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

typedef unsigned char byte;

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

Live Demo

#import <Foundation/Foundation.h>

typedef struct Books {
   NSString *title;
   NSString *author;
   NSString *subject;
   int book_id;
} Book;
 
int main() {
   Book book;
   book.title = @"Objective-C Programming";
   book.author = @"TutorialsPoint";
   book.subject = @"Programming tutorial";
   book.book_id = 100;
   
   NSLog( @"Book title : %@\n", book.title);
   NSLog( @"Book author : %@\n", book.author);
   NSLog( @"Book subject : %@\n", book.subject);
   NSLog( @"Book Id : %d\n", book.book_id);

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-12 12:21:53.745 demo[31183] Book title : Objective-C Programming
2013-09-12 12:21:53.745 demo[31183] Book author : TutorialsPoint
2013-09-12 12:21:53.745 demo[31183] Book subject : Programming tutorial
2013-09-12 12:21:53.745 demo[31183] Book Id : 100

typedef против #define

#Define – это директива Objective-C, которая также используется для определения псевдонимов для различных типов данных, аналогичных typedef, но со следующими различиями:

  • Функция typedef ограничена предоставлением символических имен только для типов, тогда как #define также может использоваться для определения псевдонима для значений, например, вы можете определить 1 как ONE и т. Д.

  • Интерпретация typedef выполняется компилятором, где операторы #define обрабатываются препроцессором.

Функция typedef ограничена предоставлением символических имен только для типов, тогда как #define также может использоваться для определения псевдонима для значений, например, вы можете определить 1 как ONE и т. Д.

Интерпретация typedef выполняется компилятором, где операторы #define обрабатываются препроцессором.

Ниже приведено простейшее использование #define –

Live Demo

#import <Foundation/Foundation.h>
 
#define TRUE  1
#define FALSE 0
 
int main( ) {
   NSLog( @"Value of TRUE : %d\n", TRUE);
   NSLog( @"Value of FALSE : %d\n", FALSE);

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-12 12:23:37.993 demo[5160] Value of TRUE : 1
2013-09-12 12:23:37.994 demo[5160] Value of FALSE : 0

Objective-C Тип Кастинг

Приведение типов – это способ преобразования переменной из одного типа данных в другой тип данных. Например, если вы хотите сохранить длинное значение в простое целое число, вы можете набрать cast long для int. Вы можете явно преобразовать значения из одного типа в другой, используя оператор приведения, следующим образом:

(type_name) expression

В Objective-C мы обычно используем CGFloat для выполнения операции с плавающей запятой, которая получена из базового типа с плавающей запятой в случае 32-разрядного и двойного в случае 64-разрядного. Рассмотрим следующий пример, где оператор приведения заставляет деление одной целочисленной переменной на другую как операцию с плавающей запятой:

Live Demo

#import <Foundation/Foundation.h>

int main() {
   int sum = 17, count = 5;
   CGFloat mean;

   mean = (CGFloat) sum / count;
   NSLog(@"Value of mean : %f\n", mean );

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-11 01:35:40.047 demo[20634] Value of mean : 3.400000

Здесь следует отметить, что оператор приведения имеет приоритет над делением, поэтому значение суммы сначала преобразуется в тип double и, наконец, оно делится на число, что приводит к двойному значению.

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

Целочисленное продвижение

Целочисленное продвижение – это процесс, при котором значения целочисленного типа «меньше», чем int или unsigned int , преобразуются либо в int, либо в unsigned int . Рассмотрим пример добавления символа в int –

Live Demo

#import <Foundation/Foundation.h>

int main() {
   int  i = 17;
   char c = 'c';  /* ascii value is 99 */
   int sum;

   sum = i + c;
   NSLog(@"Value of sum : %d\n", sum );

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-11 01:38:28.492 demo[980] Value of sum : 116

Здесь значение sum приходит как 116, потому что компилятор выполняет целочисленное продвижение и преобразует значение ‘c’ в ascii перед выполнением фактической операции сложения.

Обычное Арифметическое Преобразование

Обычные арифметические преобразования неявно выполняются для приведения их значений в общий тип. Компилятор сначала выполняет целочисленное продвижение , если операнды по-прежнему имеют разные типы, тогда они преобразуются в тип, который отображается наивысшим в следующей иерархии:

Обычное Арифметическое Преобразование

Обычные арифметические преобразования не выполняются ни для операторов присваивания, ни для логических операторов && и ||. Давайте возьмем следующий пример, чтобы понять концепцию –

Live Demo

#import <Foundation/Foundation.h>

int main() {
   int  i = 17;
   char c = 'c';  /* ascii value is 99 */
   CGFloat sum;

   sum = i + c;
   NSLog(@"Value of sum : %f\n", sum );
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-11 01:41:39.192 demo[15351] Value of sum : 116.000000

Здесь легко понять, что первый c преобразуется в целое число, но поскольку конечное значение равно float, то применяется обычное арифметическое преобразование, и компилятор преобразует i и c в float и добавляет их, получая результат float.

Objective-C Журнал Обработка

NSLog метод

Для печати журналов мы используем метод NSLog на языке программирования Objective-C, который мы использовали прямо из примера Hello World.

Давайте посмотрим на простой код, который будет печатать слова «Hello World» –

Live Demo

#import <Foundation/Foundation.h>

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

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-16 00:32:50.888 demo[16669] Hello, World! 

Отключение журналов в Live-приложениях

Так как NSLogs мы используем в нашем приложении, он будет напечатан в журналах устройства, и нецелесообразно печатать журналы в живом билде. Следовательно, мы используем определение типа для печати журналов и можем использовать их, как показано ниже.

Live Demo

#import <Foundation/Foundation.h>

#if DEBUG == 0
#define DebugLog(...)
#elif DEBUG == 1
#define DebugLog(...) NSLog(__VA_ARGS__)
#endif

int main() {
   DebugLog(@"Debug log, our custom addition gets \
   printed during debug only" );
   NSLog(@"NSLog gets printed always" );     
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу в режиме отладки, мы получим следующий результат.

2013-09-11 02:47:07.723 demo[618] Debug log, our custom addition gets printed during debug only
2013-09-11 02:47:07.723 demo[618] NSLog gets printed always

Теперь, когда мы скомпилируем и запустим программу в режиме выпуска, мы получим следующий результат.

2013-09-11 02:47:45.248 demo[3158] NSLog gets printed always

Обработка ошибок Objective C

В программировании Objective C обработка ошибок обеспечивается классом NSError, доступным в платформе Основы.

Объект NSError инкапсулирует более обширную и расширяемую информацию об ошибках, чем это возможно, используя только код ошибки или строку ошибки. Основными атрибутами объекта NSError являются домен ошибок (представленный строкой), код ошибки, специфичный для домена, и словарь информации о пользователе, содержащий информацию, специфичную для приложения.

NSError

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

NSError Object состоит из –

  • Домен – домен ошибки может быть одним из предопределенных доменов NSError или произвольной строкой, описывающей пользовательский домен, и домен не должен быть равен нулю.

  • Код – код ошибки для ошибки.

  • Информация о пользователе – словарь userInfo для ошибки и userInfo может быть ноль.

Домен – домен ошибки может быть одним из предопределенных доменов NSError или произвольной строкой, описывающей пользовательский домен, и домен не должен быть равен нулю.

Код – код ошибки для ошибки.

Информация о пользователе – словарь userInfo для ошибки и userInfo может быть ноль.

В следующем примере показано, как создать пользовательскую ошибку

NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
NSString *desc = NSLocalizedString(@"Unable to complete the process", @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];

Вот полный код вышеприведенного примера ошибки, переданного как ссылка на указатель:

Live Demo

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
-(NSString *) getEmployeeNameForID🙁int) id withError🙁NSError **)errorPtr;
@end

@implementation SampleClass

-(NSString *) getEmployeeNameForID🙁int) id withError🙁NSError **)errorPtr {
   if(id == 1) {
      return @"Employee Test Name";
   } else {
      NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
      NSString *desc =@"Unable to complete the process";
      NSDictionary *userInfo = [[NSDictionary alloc] 
      initWithObjectsAndKeys:desc,
      @"NSLocalizedDescriptionKey",NULL];  
      *errorPtr = [NSError errorWithDomain:domain code:-101 
      userInfo:userInfo];
      return @"";
   }
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   SampleClass *sampleClass = [[SampleClass alloc]init];
   NSError *error = nil;
   NSString *name1 = [sampleClass getEmployeeNameForID:1 withError:&error];
  
   if(error) {
      NSLog(@"Error finding Name1: %@",error);
   } else {
      NSLog(@"Name1: %@",name1);
   }
   
   error = nil;
   NSString *name2 = [sampleClass getEmployeeNameForID:2 withError:&error];

   if(error) {
      NSLog(@"Error finding Name2: %@",error);
   } else {
      NSLog(@"Name2: %@",name2);
   }

   [pool drain];
   return 0; 
}

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

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-14 18:01:00.809 demo[27632] Name1: Employee Test Name
2013-09-14 18:01:00.809 demo[27632] Error finding Name2: Unable to complete the process

Аргументы командной строки

Можно передать некоторые значения из командной строки программам Objective-C при их выполнении. Эти значения называются аргументами командной строки, и часто они важны для вашей программы, особенно когда вы хотите управлять своей программой извне, а не жестко кодировать эти значения внутри кода.

Аргументы командной строки обрабатываются с помощью аргументов функции main (), где argc ссылается на количество переданных аргументов, а argv [] – массив указателей, который указывает на каждый аргумент, переданный программе. Ниже приведен простой пример, который проверяет, есть ли какой-либо аргумент из командной строки, и предпринимает соответствующие действия:

#import <Foundation/Foundation.h>

int main( int argc, char *argv[] ) {
   if( argc == 2 ) {
      NSLog(@"The argument supplied is %s\n", argv[1]);
   } else if( argc > 2 ) {
      NSLog(@"Too many arguments supplied.\n");
   } else {
      NSLog(@"One argument expected.\n");
   }
}

Когда приведенный выше код компилируется и выполняется с одним аргументом, скажем, «тестирование», он дает следующий результат.

2013-09-13 03:01:17.333 demo[7640] The argument supplied is testing

Когда приведенный выше код компилируется и выполняется с двумя аргументами, скажем, testing1 и testing2, он дает следующий результат.

2013-09-13 03:01:18.333 demo[7640] Too many arguments supplied.

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

2013-09-13 03:01:18.333 demo[7640] One argument expected

Следует отметить, что argv [0] содержит имя самой программы, а argv [1] – указатель на первый предоставленный аргумент командной строки, а * argv [n] – последний аргумент. Если аргументы не предоставлены, argc будет один, иначе, если вы передадите один аргумент, тогда argc будет установлен в 2.

Вы передаете все аргументы командной строки, разделенные пробелом, но если сам аргумент имеет пробел, вы можете передать такие аргументы, заключив их в двойные кавычки “” или одинарные кавычки “. Давайте еще раз напишем приведенный выше пример, где мы напечатаем имя программы, а также передадим аргумент командной строки, заключив в двойные кавычки –

Live Demo

#import <Foundation/Foundation.h>

int main( int argc, char *argv[] ) {
   NSLog(@"Program name %s\n", argv[0]);
 
   if( argc == 2 ) {
      NSLog(@"The argument supplied is %s\n", argv[1]);
   } else if( argc > 2 ) {
      NSLog(@"Too many arguments supplied.\n");
   } else {
      NSLog(@"One argument expected.\n");
   }
   
   return 0;
}

Когда вышеприведенный код компилируется и выполняется с одним аргументом, разделенным пробелом, но внутри двойных кавычек с надписью «Testing1 Testing2», он дает следующий результат.

2017-11-30 06:36:59.081 main[71010] Program name main
2017-11-30 06:36:59.082 main[71010] One argument expected.

Objective-C Классы и Объекты

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

Класс используется для указания формы объекта, и он объединяет представление данных и методы для манипулирования этими данными в одном аккуратном пакете. Данные и методы в классе называются членами класса.

Характеристики Objective-C

  • Класс определяется в двух разных разделах, а именно: @interface и @implementation .

  • Почти все в форме объектов.

  • Объекты получают сообщения, а объекты часто называют получателями.

  • Объекты содержат переменные экземпляра.

  • Объекты и переменные экземпляра имеют область видимости.

  • Классы скрывают реализацию объекта.

  • Свойства используются для предоставления доступа к переменным экземпляра класса в других классах.

Класс определяется в двух разных разделах, а именно: @interface и @implementation .

Почти все в форме объектов.

Объекты получают сообщения, а объекты часто называют получателями.

Объекты содержат переменные экземпляра.

Объекты и переменные экземпляра имеют область видимости.

Классы скрывают реализацию объекта.

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

Определения класса Objective-C

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

Определение класса начинается с ключевого слова @interface, за которым следует имя интерфейса (класса); и тело класса, заключенное в пару фигурных скобок. В Objective-C все классы являются производными от базового класса, называемого NSObject . Это суперкласс всех классов Objective-C. Он предоставляет основные методы, такие как выделение памяти и инициализация. Например, мы определили тип данных Box, используя ключевое слово class следующим образом:

@interface Box:NSObject {
   //Instance variables
   double length;    // Length of a box
   double breadth;   // Breadth of a box
}
@property(nonatomic, readwrite) double height;  // Property

@end

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

Выделение и инициализация объектов Objective C

Класс предоставляет чертежи для объектов, поэтому в основном объект создается из класса. Мы объявляем объекты класса точно так же, как мы объявляем переменные базовых типов. Следующие операторы объявляют два объекта класса Box –

Box box1 = [[Box alloc]init];     // Create box1 object of type Box
Box box2 = [[Box alloc]init];     // Create box2 object of type Box

Оба объекта box1 и box2 будут иметь свою собственную копию элементов данных.

Доступ к членам данных

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

Live Demo

#import <Foundation/Foundation.h>

@interface Box:NSObject {
   double length;    // Length of a box
   double breadth;   // Breadth of a box
   double height;    // Height of a box
}

@property(nonatomic, readwrite) double height;  // Property
-(double) volume;
@end

@implementation Box

@synthesize height; 

-(id)init {
   self = [super init];
   length = 1.0;
   breadth = 1.0;
   return self;
}

-(double) volume {
   return length*breadth*height;
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
   Box *box1 = [[Box alloc]init];    // Create box1 object of type Box
   Box *box2 = [[Box alloc]init];    // Create box2 object of type Box

   double volume = 0.0;             // Store the volume of a box here
 
   // box 1 specification
   box1.height = 5.0; 

   // box 2 specification
   box2.height = 10.0;
  
   // volume of box 1
   volume = [box1 volume];
   NSLog(@"Volume of Box1 : %f", volume);
   
   // volume of box 2
   volume = [box2 volume];
   NSLog(@"Volume of Box2 : %f", volume);
   
   [pool drain];
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-22 21:25:33.314 ClassAndObjects[387:303] Volume of Box1 : 5.000000
2013-09-22 21:25:33.316 ClassAndObjects[387:303] Volume of Box2 : 10.000000

свойства

Свойства введены в Objective-C, чтобы обеспечить доступ к переменной экземпляра класса вне класса.

Различные части объявления свойства заключаются в следующем.

  • Свойства начинаются с @property , который является ключевым словом

  • За ним следуют спецификаторы доступа, которые являются неатомарными или атомарными, доступны для чтения или записи и только для чтения, сильные, unsafe_unretained или слабые. Это зависит от типа переменной. Для любого типа указателя мы можем использовать сильный, unsafe_unretained или слабый. Аналогично для других типов мы можем использовать readwrite или readonly.

  • Далее следует тип данных переменной.

  • Наконец, у нас есть имя свойства, оканчивающееся точкой с запятой.

  • Мы можем добавить синтезирующий оператор в классе реализации. Но в последнем XCode, часть синтеза позаботилась о XCode, и вам не нужно включать оператор синтеза.

Свойства начинаются с @property , который является ключевым словом

За ним следуют спецификаторы доступа, которые являются неатомарными или атомарными, доступны для чтения или записи и только для чтения, сильные, unsafe_unretained или слабые. Это зависит от типа переменной. Для любого типа указателя мы можем использовать сильный, unsafe_unretained или слабый. Аналогично для других типов мы можем использовать readwrite или readonly.

Далее следует тип данных переменной.

Наконец, у нас есть имя свойства, оканчивающееся точкой с запятой.

Мы можем добавить синтезирующий оператор в классе реализации. Но в последнем XCode, часть синтеза позаботилась о XCode, и вам не нужно включать оператор синтеза.

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

Например, предположим, что у нас есть свойство @property (nonatomic, readonly) BOOL isDone . Под капотом находятся сеттеры и геттеры, созданные как показано ниже.

-(void)setIsDone(BOOL)isDone;
-(BOOL)isDone;

Objective-C Наследование

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

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

Идея наследования реализует отношения. Например, млекопитающее IS-A животное, собака IS-A млекопитающее, следовательно, собака IS-A животное, а также и так далее.

Базовые и производные классы

Objective-C допускает только многоуровневое наследование, т. Е. Он может иметь только один базовый класс, но допускает многоуровневое наследование. Все классы в Objective-C являются производными от суперкласса NSObject .

@interface derived-class: base-class

Рассмотрим базовый класс Person и его производный класс Employee следующим образом:

Live Demo

#import <Foundation/Foundation.h>
 
@interface Person : NSObject {
   NSString *personName;
   NSInteger personAge;
}

- (id)initWithName🙁NSString *)name andAge🙁NSInteger)age;
- (void)print;

@end

@implementation Person

- (id)initWithName🙁NSString *)name andAge🙁NSInteger)age {
   personName = name;
   personAge = age;
   return self;
}

- (void)print {
   NSLog(@"Name: %@", personName);
   NSLog(@"Age: %ld", personAge);
}

@end

@interface Employee : Person {
   NSString *employeeEducation;
}

- (id)initWithName🙁NSString *)name andAge🙁NSInteger)age 
  andEducation🙁NSString *)education;
- (void)print;
@end

@implementation Employee

- (id)initWithName🙁NSString *)name andAge🙁NSInteger)age 
   andEducation: (NSString *)education {
      personName = name;
      personAge = age;
      employeeEducation = education;
      return self;
   }

- (void)print {
   NSLog(@"Name: %@", personName);
   NSLog(@"Age: %ld", personAge);
   NSLog(@"Education: %@", employeeEducation);
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];        
   NSLog(@"Base class Person Object");
   Person *person = [[Person alloc]initWithName:@"Raj" andAge:5];
   [person print];
   NSLog(@"Inherited Class Employee Object");
   Employee *employee = [[Employee alloc]initWithName:@"Raj" 
   andAge:5 andEducation:@"MBA"];
   [employee print];        
   [pool drain];
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-22 21:20:09.842 Inheritance[349:303] Base class Person Object
2013-09-22 21:20:09.844 Inheritance[349:303] Name: Raj
2013-09-22 21:20:09.844 Inheritance[349:303] Age: 5
2013-09-22 21:20:09.845 Inheritance[349:303] Inherited Class Employee Object
2013-09-22 21:20:09.845 Inheritance[349:303] Name: Raj
2013-09-22 21:20:09.846 Inheritance[349:303] Age: 5
2013-09-22 21:20:09.846 Inheritance[349:303] Education: MBA

Контроль доступа и наследование

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

Мы можем суммировать различные типы доступа в зависимости от того, кто может получить к ним доступ следующим образом –

Производный класс наследует все методы и переменные базового класса со следующими исключениями:

  • Переменные, объявленные в файле реализации с помощью расширений, недоступны.

  • Методы, объявленные в файле реализации с помощью расширений, недоступны.

  • Если унаследованный класс реализует метод в базовом классе, то выполняется метод в производном классе.

Переменные, объявленные в файле реализации с помощью расширений, недоступны.

Методы, объявленные в файле реализации с помощью расширений, недоступны.

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

Objective-C Полиморфизм

Слово полиморфизм означает наличие многих форм. Как правило, полиморфизм возникает, когда существует иерархия классов, и они связаны наследованием.

Полиморфизм Objective-C означает, что вызов функции-члена приведет к выполнению другой функции в зависимости от типа объекта, который вызывает функцию.

Рассмотрим пример, у нас есть класс Shape, который предоставляет базовый интерфейс для всех фигур. Квадрат и прямоугольник являются производными от базового класса Shape.

У нас есть метод printArea, который собирается показать о полиморфизме функции ООП.

Live Demo

#import <Foundation/Foundation.h>

@interface Shape : NSObject {
   CGFloat area;
}

- (void)printArea;
- (void)calculateArea;
@end

@implementation Shape
- (void)printArea {
   NSLog(@"The area is %f", area);
}

- (void)calculateArea {

}

@end

@interface Square : Shape {
   CGFloat length;
}

- (id)initWithSide🙁CGFloat)side;
- (void)calculateArea;

@end

@implementation Square
- (id)initWithSide🙁CGFloat)side {
   length = side;
   return self;
}

- (void)calculateArea {
   area = length * length;
}

- (void)printArea {
   NSLog(@"The area of square is %f", area);
}

@end

@interface Rectangle : Shape {
   CGFloat length;
   CGFloat breadth;
}

- (id)initWithLength🙁CGFloat)rLength andBreadth🙁CGFloat)rBreadth;
@end

@implementation Rectangle
- (id)initWithLength🙁CGFloat)rLength andBreadth🙁CGFloat)rBreadth {
   length = rLength;
   breadth = rBreadth;
   return self;
}

- (void)calculateArea {
   area = length * breadth;
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   Shape *square = [[Square alloc]initWithSide:10.0];
   [square calculateArea];
   [square printArea];
   Shape *rect = [[Rectangle alloc]
   initWithLength:10.0 andBreadth:5.0];
   [rect calculateArea];
   [rect printArea];        
   [pool drain];
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-22 21:21:50.785 Polymorphism[358:303] The area of square is 100.000000
2013-09-22 21:21:50.786 Polymorphism[358:303] The area is 50.000000

В вышеприведенном примере, основанном на доступности методов executeArea и printArea, выполняется либо метод в базовом классе, либо производный класс.

Полиморфизм обрабатывает переключение методов между базовым классом и производным классом на основе реализации метода двух классов.

Инкапсуляция данных в Objective-C

Все программы Objective-C состоят из следующих двух основных элементов:

  • Программные операторы (код) – это часть программы, которая выполняет действия, и они называются методами.

  • Данные программы. Данные – это информация о программе, на которую влияют функции программы.

Программные операторы (код) – это часть программы, которая выполняет действия, и они называются методами.

Данные программы. Данные – это информация о программе, на которую влияют функции программы.

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

Инкапсуляция данных – это механизм связывания данных и функций, которые их используют, а абстракция данных – это механизм, который раскрывает только интерфейсы и скрывает детали реализации от пользователя.

Objective-C поддерживает свойства инкапсуляции и сокрытия данных посредством создания пользовательских типов, называемых классами . Например –

@interface Adder : NSObject {
   NSInteger total;
}

- (id)initWithInitialNumber🙁NSInteger)initialNumber;
- (void)addNumber🙁NSInteger)newNumber;
- (NSInteger)getTotal;

@end

Переменная total является закрытой, и мы не можем получить доступ извне класса. Это означает, что к ним могут получить доступ только другие члены класса Adder, а не любая другая часть вашей программы. Это один из способов инкапсуляции.

Методы внутри файла интерфейса доступны и являются общедоступными.

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

Пример инкапсуляции данных

Любая программа Objective-C, в которой вы реализуете класс с переменными открытого и закрытого членов, является примером инкапсуляции данных и абстракции данных. Рассмотрим следующий пример –

Live Demo

#import <Foundation/Foundation.h>

@interface Adder : NSObject {
   NSInteger total;
}

- (id)initWithInitialNumber🙁NSInteger)initialNumber;
- (void)addNumber🙁NSInteger)newNumber;
- (NSInteger)getTotal;

@end

@implementation Adder
-(id)initWithInitialNumber🙁NSInteger)initialNumber {
   total = initialNumber;
   return self;
}

- (void)addNumber🙁NSInteger)newNumber {
   total = total + newNumber;
}

- (NSInteger)getTotal {
   return total;
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];        
   Adder *adder = [[Adder alloc]initWithInitialNumber:10];
   [adder addNumber:5];
   [adder addNumber:4];
   
   NSLog(@"The total is %ld",[adder getTotal]);
   [pool drain];
   return 0;
}

Когда приведенный выше код компилируется и выполняется, он дает следующий результат –

2013-09-22 21:17:30.485 DataEncapsulation[317:303] The total is 19

Выше класс складывает числа вместе и возвращает сумму. Открытые члены addNum и getTotal являются интерфейсами с внешним миром, и пользователь должен знать их, чтобы использовать класс. Общее число закрытых членов – это то, что скрыто от внешнего мира, но необходимо для правильной работы класса.

Разработка стратегии

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

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

Objective-C Категории

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

Если вам нужно добавить метод к существующему классу, возможно, чтобы добавить функциональность, чтобы упростить выполнение чего-либо в вашем собственном приложении, самый простой способ – использовать категорию.

Синтаксис для объявления категории использует ключевое слово @interface, как и стандартное описание класса Objective-C, но не указывает на какое-либо наследование от подкласса. Вместо этого в скобках указывается название категории, например:

@interface ClassName (CategoryName)

@end

Характеристики категории

  • Категория может быть объявлена ​​для любого класса, даже если у вас нет исходного кода реализации.

  • Любые методы, которые вы объявляете в категории, будут доступны для всех экземпляров исходного класса, а также для любых подклассов исходного класса.

  • Во время выполнения нет никакой разницы между методом, добавленным категорией, и тем, который реализован исходным классом.

Категория может быть объявлена ​​для любого класса, даже если у вас нет исходного кода реализации.

Любые методы, которые вы объявляете в категории, будут доступны для всех экземпляров исходного класса, а также для любых подклассов исходного класса.

Во время выполнения нет никакой разницы между методом, добавленным категорией, и тем, который реализован исходным классом.

Теперь давайте посмотрим на пример реализации категории. Давайте добавим категорию к классу какао NSString. Эта категория позволит нам добавить новый метод getCopyRightString, который поможет нам вернуть строку авторских прав. Это показано ниже.

Live Demo

#import <Foundation/Foundation.h>

@interface NSString(MyAdditions)
+(NSString *)getCopyRightString;
@end

@implementation NSString(MyAdditions)

+(NSString *)getCopyRightString {
   return @"Copyright TutorialsPoint.com 2013";
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSString *copyrightString = [NSString getCopyRightString];
   NSLog(@"Accessing Category: %@",copyrightString);
   
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-22 21:19:12.125 Categories[340:303] Accessing Category: Copyright TutorialsPoint.com 2013

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

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

Objective-C Позирует

Прежде чем приступить к Posing в Objective-C, я хотел бы обратить ваше внимание на то, что Posing объявлен устаревшим в Mac OS X 10.5 и после этого не будет доступен для использования. Так что для тех, кто не обеспокоен этими устаревшими методами, можете пропустить эту главу.

Objective-C позволяет классу полностью заменить другой класс в программе. Говорят, что замещающий класс «представляет собой» целевой класс.

Для версий, которые поддерживали позирование, все сообщения, отправленные целевому классу, вместо этого принимаются классом позирования.

NSObject содержит poseAsClass: метод, который позволяет нам заменить существующий класс, как сказано выше.

Ограничения в позе

  • Класс может представлять собой только один из своих прямых или косвенных суперклассов.

  • Класс позирования не должен определять какие-либо новые переменные экземпляра, которые отсутствуют в целевом классе (хотя он может определять или переопределять методы).

  • Целевой класс, возможно, не получил никаких сообщений до постановки.

  • Класс представления может вызывать переопределенные методы через super, включая реализацию целевого класса.

  • Класс позирования может переопределять методы, определенные в категориях.

Класс может представлять собой только один из своих прямых или косвенных суперклассов.

Класс позирования не должен определять какие-либо новые переменные экземпляра, которые отсутствуют в целевом классе (хотя он может определять или переопределять методы).

Целевой класс, возможно, не получил никаких сообщений до постановки.

Класс представления может вызывать переопределенные методы через super, включая реализацию целевого класса.

Класс позирования может переопределять методы, определенные в категориях.

#import <Foundation/Foundation.h>

@interface MyString : NSString

@end

@implementation MyString

- (NSString *)stringByReplacingOccurrencesOfString🙁NSString *)target
withString🙁NSString *)replacement {
   NSLog(@"The Target string is %@",target);
   NSLog(@"The Replacement string is %@",replacement);
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   [MyString poseAsClass:[NSString class]];
   NSString *string = @"Test";
   [string stringByReplacingOccurrencesOfString:@"a" withString:@"c"];
   
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу в более старой Mac OS X (V_10.5 или более ранней), мы получим следующий результат.

2013-09-22 21:23:46.829 Posing[372:303] The Target string is a
2013-09-22 21:23:46.830 Posing[372:303] The Replacement string is c

В приведенном выше примере мы просто загрязнили исходный метод нашей реализацией, и это повлияет на все операции NSString с помощью вышеуказанного метода.

Расширения Objective-C

Расширение класса имеет некоторое сходство с категорией, но оно может быть добавлено только к классу, для которого у вас есть исходный код во время компиляции (класс компилируется одновременно с расширением класса).

Методы, объявленные расширением класса, реализованы в блоке реализации исходного класса, поэтому вы не можете, например, объявить расширение класса в классе платформы, таком как класс Touch Cocoa или Cocoa, например NSString.

Расширения на самом деле являются категориями без названия категории. Это часто называют анонимными категориями .

Синтаксис для объявления расширения использует ключевое слово @interface, как и стандартное описание класса Objective-C, но не указывает на какое-либо наследование от подкласса. Вместо этого он просто добавляет скобки, как показано ниже –

@interface ClassName ()

@end

Характеристики расширений

  • Расширение не может быть объявлено ни для какого класса, только для классов, которые имеют оригинальную реализацию исходного кода.

  • Расширение добавляет частные методы и частные переменные, которые относятся только к классу.

  • Любой метод или переменная, объявленная внутри расширений, недоступна даже для унаследованных классов.

Расширение не может быть объявлено ни для какого класса, только для классов, которые имеют оригинальную реализацию исходного кода.

Расширение добавляет частные методы и частные переменные, которые относятся только к классу.

Любой метод или переменная, объявленная внутри расширений, недоступна даже для унаследованных классов.

Пример расширений

Давайте создадим класс SampleClass, который имеет расширение. В расширении у нас будет приватная переменная internalID.

Затем, давайте получим метод getExternalID, который возвращает externalID после обработки internalID.

Пример показан ниже, и он не будет работать на онлайн-компиляторе.

#import <Foundation/Foundation.h>

@interface SampleClass : NSObject {
   NSString *name;
}

- (void)setInternalID;
- (NSString *)getExternalID;

@end

@interface SampleClass() {
   NSString *internalID;
}

@end

@implementation SampleClass

- (void)setInternalID {
   internalID = [NSString stringWithFormat: 
   @"UNIQUEINTERNALKEY%dUNIQUEINTERNALKEY",arc4random()%100];
}

- (NSString *)getExternalID {
   return [internalID stringByReplacingOccurrencesOfString: 
   @"UNIQUEINTERNALKEY" withString:@""];
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass setInternalID];
   NSLog(@"ExternalID: %@",[sampleClass getExternalID]);        
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-22 21:18:31.754 Extensions[331:303] ExternalID: 51

В приведенном выше примере мы видим, что internalID не возвращается напрямую. Здесь мы удаляем UNIQUEINTERNALKEY и делаем оставшееся значение доступным для метода getExternalID.

В приведенном выше примере используется только строковая операция, но она может иметь множество функций, таких как шифрование / дешифрование и так далее.

Протоколы Objective-C

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

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

Синтаксис протокола показан ниже.

@protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end

Методы под ключевым словом @required должны быть реализованы в классах, соответствующих протоколу, а методы под ключевым словом @optional являются необязательными для реализации.

Вот синтаксис для класса, соответствующего протоколу

@interface MyClass : NSObject <MyProtocol>
...
@end

Это означает, что любой экземпляр MyClass будет реагировать не только на методы, объявленные специально в интерфейсе, но и на то, что MyClass также обеспечивает реализацию необходимых методов в MyProtocol. Нет необходимости переопределять методы протокола в интерфейсе класса – достаточно принятия протокола.

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

Пример показан ниже.

Live Demo

#import <Foundation/Foundation.h>

@protocol PrintProtocolDelegate
- (void)processCompleted;

@end

@interface PrintClass :NSObject {
   id delegate;
}

- (void) printDetails;
- (void) setDelegate🙁id)newDelegate;
@end

@implementation PrintClass
- (void)printDetails {
   NSLog(@"Printing Details");
   [delegate processCompleted];
}

- (void) setDelegate🙁id)newDelegate {
   delegate = newDelegate;
}

@end

@interface SampleClass:NSObject<PrintProtocolDelegate>
- (void)startAction;

@end

@implementation SampleClass
- (void)startAction {
   PrintClass *printClass = [[PrintClass alloc]init];
   [printClass setDelegate:self];
   [printClass printDetails];
}

-(void)processCompleted {
   NSLog(@"Printing Process Completed");
}

@end

int main(int argc, const char * argv[]) {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass startAction];
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-22 21:15:50.362 Protocols[275:303] Printing Details
2013-09-22 21:15:50.364 Protocols[275:303] Printing Process Completed

В приведенном выше примере мы увидели, как методы delgate вызываются и выполняются. Он начинается с startAction, после завершения процесса вызывается метод делегата processCompleted для информирования о завершении операции.

В любом приложении iOS или Mac у нас никогда не будет программы, реализованной без делегата. Поэтому важно понимать использование делегатов. Объекты делегатов должны использовать тип свойства unsafe_unretained во избежание утечек памяти.

Objective-C Динамическое связывание

Динамическое связывание определяет способ вызова во время выполнения, а не во время компиляции. Динамическое связывание также называется поздним связыванием.

В Objective-C все методы разрешаются динамически во время выполнения. Точный исполняемый код определяется как именем метода (селектором), так и принимающим объектом.

Динамическое связывание позволяет полиморфизм. Например, рассмотрим коллекцию объектов, включая Rectangle и Square. Каждый объект имеет свою собственную реализацию метода printArea.

В следующем фрагменте кода фактический код, который должен выполняться выражением [anObject printArea], определяется во время выполнения. Система времени выполнения использует селектор для запуска метода, чтобы идентифицировать соответствующий метод в любом классе anObject.

Давайте посмотрим на простой код, который объясняет динамическое связывание.

Live Demo

#import <Foundation/Foundation.h>

@interface Square:NSObject {
   float area;
}

- (void)calculateAreaOfSide🙁CGFloat)side;
- (void)printArea;
@end

@implementation Square
- (void)calculateAreaOfSide🙁CGFloat)side {
   area = side * side;
}

- (void)printArea {
   NSLog(@"The area of square is %f",area);
}

@end

@interface Rectangle:NSObject {
   float area;
}

- (void)calculateAreaOfLength🙁CGFloat)length andBreadth🙁CGFloat)breadth;
- (void)printArea;
@end

@implementation  Rectangle

- (void)calculateAreaOfLength🙁CGFloat)length andBreadth🙁CGFloat)breadth {
   area = length * breadth;
}

- (void)printArea {
   NSLog(@"The area of Rectangle is %f",area);
}

@end

int main() {
   Square *square = [[Square alloc]init];
   [square calculateAreaOfSide:10.0];
   
   Rectangle *rectangle = [[Rectangle alloc]init];
   [rectangle calculateAreaOfLength:10.0 andBreadth:5.0];
   
   NSArray *shapes = [[NSArray alloc]initWithObjects: square, rectangle,nil];
   id object1 = [shapes objectAtIndex:0];
   [object1 printArea];
   
   id object2 = [shapes objectAtIndex:1];
   [object2 printArea];
   
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-28 07:42:29.821 demo[4916] The area of square is 100.000000
2013-09-28 07:42:29.821 demo[4916] The area of Rectangle is 50.000000

Как видно из приведенного выше примера, метод printArea динамически выбирается во время выполнения. Это пример динамического связывания и весьма полезен во многих ситуациях при работе с объектами подобного типа.

Objective-C Композитные объекты

Мы можем создать подкласс в кластере классов, который определяет класс, который встраивает в него объект. Эти объекты класса являются составными объектами.

Так что вам может быть интересно, что такое кластер классов. Итак, сначала мы увидим, что такое кластер классов.

Кластеры классов

Кластеры классов – это шаблон проектирования, который среда Foundation широко использует. Кластеры классов объединяют несколько частных конкретных подклассов в открытый абстрактный суперкласс. Группирование классов таким образом упрощает общедоступную архитектуру объектно-ориентированной среды, не уменьшая ее функциональное богатство. Кластеры классов основаны на шаблоне проектирования Abstract Factory.

Чтобы упростить задачу, вместо создания нескольких классов для похожих функций мы создадим один класс, который позаботится о его обработке на основе значения ввода.

Например, в NSNumber у нас есть много кластеров классов, таких как char, int, bool и так далее. Мы группируем их все в один класс, который заботится об обработке подобных операций в одном классе. NSNumber фактически оборачивает значение этих примитивных типов в объекты.

Так что же такое составной объект?

Встраивая объект частного кластера в объект нашего собственного дизайна, мы создаем составной объект. Этот составной объект может полагаться на кластерный объект для его основной функциональности, перехватывая только те сообщения, которые составной объект хочет обработать определенным образом. Эта архитектура уменьшает объем кода, который мы должны написать, и позволяет вам использовать протестированный код, предоставляемый Foundation Framework.

Это объясняется на следующем рисунке.

Objective-C Композитные объекты

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

Метод count класса NSArray является примером; реализация метода объекта, который он переопределяет, может быть настолько простой, как:

- (unsigned)count  {
   return [embeddedObject count];
}

В приведенном выше примере встроенный объект на самом деле имеет тип NSArray.

Пример составного объекта

Теперь, чтобы увидеть полный пример, давайте посмотрим на пример из документации Apple, которая приведена ниже.

Live Demo

#import <Foundation/Foundation.h>

@interface ValidatingArray : NSMutableArray {
   NSMutableArray *embeddedArray;
}

+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex🙁unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex🙁unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex🙁unsigned)index;
- (void)removeObjectAtIndex🙁unsigned)index;

@end

@implementation ValidatingArray
- init {
   self = [super init];
   if (self) {
      embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
   }
   return self;
}

+ validatingArray {
   return [[self alloc] init] ;
}

- (unsigned)count {
   return [embeddedArray count];
}

- objectAtIndex🙁unsigned)index {
   return [embeddedArray objectAtIndex:index];
}

- (void)addObject🙁id)object {
   if (object != nil) {
      [embeddedArray addObject:object];
   }
}

- (void)replaceObjectAtIndex🙁unsigned)index withObject🙁id)object; {
   if (index <[embeddedArray count] && object != nil) {
      [embeddedArray replaceObjectAtIndex:index withObject:object];
   }
}

- (void)removeLastObject; {
   if ([embeddedArray count] > 0) {
      [embeddedArray removeLastObject];
   }
}

- (void)insertObject🙁id)object atIndex🙁unsigned)index; {
   if (object != nil) {
      [embeddedArray insertObject:object atIndex:index];
   }
}

- (void)removeObjectAtIndex🙁unsigned)index; {
   if (index <[embeddedArray count]) {
      [embeddedArray removeObjectAtIndex:index];
   }
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   ValidatingArray *validatingArray = [ValidatingArray validatingArray];
   
   [validatingArray addObject:@"Object1"];
   [validatingArray addObject:@"Object2"];
   [validatingArray addObject:[NSNull null]];
   [validatingArray removeObjectAtIndex:2];
   NSString *aString = [validatingArray objectAtIndex:1];
   NSLog(@"The value at Index 1 is %@",aString);
   [pool drain];
   
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-28 22:03:54.294 demo[6247] The value at Index 1 is Object2

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

Основы Obj-C Foundation

Если вы ссылаетесь на документацию Apple, вы можете увидеть подробности платформы Foundation, как указано ниже.

Платформа Foundation определяет базовый уровень классов Objective-C. В дополнение к предоставлению набора полезных примитивных классов объектов, он вводит несколько парадигм, которые определяют функциональные возможности, не охватываемые языком Objective-C. Основа фонда разработана с учетом этих целей –

  • Предоставить небольшой набор базовых служебных классов.

  • Упростите разработку программного обеспечения, введя согласованные соглашения для таких вещей, как освобождение.

  • Поддержка Unicode-строк, постоянство объектов и распределение объектов.

  • Обеспечение уровня независимости ОС для повышения мобильности.

Предоставить небольшой набор базовых служебных классов.

Упростите разработку программного обеспечения, введя согласованные соглашения для таких вещей, как освобождение.

Поддержка Unicode-строк, постоянство объектов и распределение объектов.

Обеспечение уровня независимости ОС для повышения мобильности.

Фреймворк был разработан NeXTStep, который был приобретен Apple, и эти базовые классы стали частью Mac OS X и iOS.

Поскольку он был разработан NeXTStep, он имеет префикс класса “NS”.

Мы использовали Foundation Framework во всех наших примерах программ. Почти необходимо использовать Foundation Framework.

Обычно мы используем что-то вроде #import <Foundation / NSString.h> для импорта класса Objective-C, но во избежание импорта слишком большого числа классов все это импортируется в #import <Foundation / Foundation.h> .

NSObject – это базовый класс всех объектов, включая классы базового набора. Он предоставляет методы для управления памятью. Он также предоставляет базовый интерфейс к системе времени выполнения и возможность вести себя как объекты Objective-C. Он не имеет базового класса и является корнем для всех классов.

Базовые классы на основе функциональности

Sr.No. Тип и описание петли
1 Хранилище данных

NSArray, NSDictionary и NSSet предоставляют хранилище для объектов Objective-C любого класса.

2 Текст и строки

NSCharacterSet представляет различные группы символов, которые используются классами NSString и NSScanner. Классы NSString представляют текстовые строки и предоставляют методы для поиска, объединения и сравнения строк. Объект NSScanner используется для сканирования чисел и слов из объекта NSString.

3 Даты и время

Классы NSDate, NSTimeZone и NSCalendar хранят время и даты и представляют календарную информацию. Они предлагают методы для расчета даты и времени. Вместе с NSLocale они предоставляют методы для отображения даты и времени во многих форматах и ​​для корректировки времени и даты в зависимости от местоположения в мире.

4 Обработка исключений

Обработка исключений используется для обработки непредвиденных ситуаций и предлагается в Objective-C с NSException.

5 Обработка файлов

Обработка файлов осуществляется с помощью класса NSFileManager.

6 Система загрузки URL

Набор классов и протоколов, обеспечивающих доступ к общим интернет-протоколам.

NSArray, NSDictionary и NSSet предоставляют хранилище для объектов Objective-C любого класса.

NSCharacterSet представляет различные группы символов, которые используются классами NSString и NSScanner. Классы NSString представляют текстовые строки и предоставляют методы для поиска, объединения и сравнения строк. Объект NSScanner используется для сканирования чисел и слов из объекта NSString.

Классы NSDate, NSTimeZone и NSCalendar хранят время и даты и представляют календарную информацию. Они предлагают методы для расчета даты и времени. Вместе с NSLocale они предоставляют методы для отображения даты и времени во многих форматах и ​​для корректировки времени и даты в зависимости от местоположения в мире.

Обработка исключений используется для обработки непредвиденных ситуаций и предлагается в Objective-C с NSException.

Обработка файлов осуществляется с помощью класса NSFileManager.

Набор классов и протоколов, обеспечивающих доступ к общим интернет-протоколам.

Objective-C Быстрое перечисление

Быстрое перечисление – это функция Objective-C, которая помогает в перечислении через коллекцию. Итак, чтобы узнать о быстром перечислении, нам нужно сначала узнать о коллекции, что будет объяснено в следующем разделе.

Коллекции в Objective-C

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

Существует несколько разных типов коллекций. Хотя все они выполняют одну и ту же цель, заключающуюся в том, что они могут содержать другие объекты, они различаются в основном способом извлечения объектов. Наиболее распространенные коллекции, используемые в Objective-C, –

  • NSSet
  • NSArray
  • NSDictionary
  • NSMutableSet
  • NSMutableArray
  • NSMutableDictionary

Если вы хотите узнать больше об этих структурах, обратитесь к хранилищу данных в Foundation Framework .

Синтаксис быстрого перечисления

for (classType variable in collectionObject ) { 
  statements 
}

Вот пример для быстрого перечисления.

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSArray *array = [[NSArray alloc]
   initWithObjects:@"string1", @"string2",@"string3",nil];
   
   for(NSString *aString in array) {
      NSLog(@"Value: %@",aString);
   }
   
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-28 06:26:22.835 demo[7426] Value: string1
2013-09-28 06:26:22.836 demo[7426] Value: string2
2013-09-28 06:26:22.836 demo[7426] Value: string3

Как вы можете видеть в выводе, каждый из объектов в массиве печатается в порядке.

Быстрое перечисление в обратном направлении

for (classType variable in [collectionObject reverseObjectEnumerator] ) { 
  statements 
}

Вот пример для reverseObjectEnumerator в быстром перечислении.

Live Demo

#import <Foundation/Foundation.h>

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSArray *array = [[NSArray alloc]
   initWithObjects:@"string1", @"string2",@"string3",nil];
   
   for(NSString *aString in [array reverseObjectEnumerator]) {
      NSLog(@"Value: %@",aString);
   }
   
   [pool drain];
   return 0;
}

Теперь, когда мы скомпилируем и запустим программу, мы получим следующий результат.

2013-09-28 06:27:51.025 demo[12742] Value: string3
2013-09-28 06:27:51.025 demo[12742] Value: string2
2013-09-28 06:27:51.025 demo[12742] Value: string1

Как видно из выходных данных, каждый из объектов в массиве печатается в обратном порядке по сравнению с обычным быстрым перечислением.

Управление памятью Obj-C

Управление памятью является одним из наиболее важных процессов в любом языке программирования. Это процесс, посредством которого память объектов выделяется, когда они требуются, и освобождается, когда они больше не требуются.

Управление памятью объекта – это вопрос производительности; если приложение не освобождает ненужные объекты, его объем памяти увеличивается, а производительность снижается.

Objective-C Методы управления памятью можно разделить на два типа.

  • «Ручное удержание-отпускание» или MRR
  • «Автоматический подсчет ссылок» или ARC

«Ручное удержание-отпускание» или MRR

В MRR мы явно управляем памятью, отслеживая объекты самостоятельно. Это реализовано с использованием модели, известной как подсчет ссылок, которую NSObject класса Foundation предоставляет в сочетании со средой выполнения.

Единственная разница между MRR и ARC заключается в том, что удержание и разблокирование обрабатываются нами вручную в первом, а в последнем автоматически.

На следующем рисунке представлен пример того, как работает управление памятью в Objective-C.

Objective-C Управление памятью

Жизненный цикл памяти объекта класса A показан на рисунке выше. Как вы можете видеть, счет сохранения отображается под объектом, когда счет сохранения объекта становится равным 0, объект полностью освобождается, и его память освобождается для использования другими объектами.

Объект класса A сначала создается с использованием метода alloc / init, доступного в NSObject. Теперь счет удержания становится 1.

Теперь класс B сохраняет объект класса A, а количество сохраняемых объектов класса A становится равным 2.

Затем класс C делает копию объекта. Теперь он создается как еще один экземпляр класса A с такими же значениями для переменных экземпляра. Здесь счет сохранения равен 1, а не счет хранения исходного объекта. Это представлено пунктирной линией на рисунке.

Скопированный объект высвобождается классом C с использованием метода release, и счет сохранения становится равным 0, и, следовательно, объект уничтожается.

В случае начального объекта класса A, счет удержания равен 2, и его необходимо дважды разблокировать, чтобы уничтожить. Это делается с помощью инструкций выпуска класса A и класса B, которые уменьшают количество сохраняемых данных до 1 и 0 соответственно. Наконец, объект уничтожен.

Основные правила MRR

  • У нас есть любой объект, который мы создаем: мы создаем объект, используя метод, имя которого начинается с «alloc», «new», «copy» или «mutableCopy»

  • Мы можем вступить во владение объектом, используя retain: полученный объект обычно гарантированно остается действительным в методе, в котором он был получен, и этот метод также может безопасно вернуть объект своему вызывающему. Мы используем удержание в двух ситуациях –

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

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

  • Когда он нам больше не нужен, мы должны отказаться от права собственности на принадлежащий нам объект: мы отказываемся от права собственности на объект, отправив ему сообщение о выпуске или сообщение об автоматическом выпуске. Таким образом, в терминологии какао отказ от владения объектом обычно называется «высвобождением» объекта.

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

У нас есть любой объект, который мы создаем: мы создаем объект, используя метод, имя которого начинается с «alloc», «new», «copy» или «mutableCopy»

Мы можем вступить во владение объектом, используя retain: полученный объект обычно гарантированно остается действительным в методе, в котором он был получен, и этот метод также может безопасно вернуть объект своему вызывающему. Мы используем удержание в двух ситуациях –

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

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

Когда он нам больше не нужен, мы должны отказаться от права собственности на принадлежащий нам объект: мы отказываемся от права собственности на объект, отправив ему сообщение о выпуске или сообщение об автоматическом выпуске. Таким образом, в терминологии какао отказ от владения объектом обычно называется «высвобождением» объекта.

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

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");
   
   // Should set the object to nil
   sampleClass = nil;
   return 0;
}

Когда мы скомпилируем вышеуказанную программу, мы получим следующий вывод.

2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this

«Автоматический подсчет ссылок» или ARC

В автоматическом подсчете ссылок или ARC система использует ту же систему подсчета ссылок, что и MRR, но она вставляет соответствующие вызовы метода управления памятью для нас во время компиляции. Мы настоятельно рекомендуем использовать ARC для новых проектов. Если мы используем ARC, обычно нет необходимости понимать основную реализацию, описанную в этом документе, хотя в некоторых ситуациях это может быть полезно. Для получения дополнительной информации о ARC см. Переход к примечаниям к выпуску ARC.

Как уже упоминалось выше, в ARC нам не нужно добавлять методы release и retain, поскольку об этом позаботится компилятор. На самом деле, основной процесс Objective-C все тот же. Он использует операции сохранения и выпуска для внутреннего использования, что облегчает разработчику кодирование, не беспокоясь об этих операциях, что уменьшит как объем написанного кода, так и возможность утечек памяти.

Был еще один принцип, называемый сборкой мусора, который используется в Mac OS-X вместе с MRR, но с момента его устаревания в OS-X Mountain Lion он не обсуждался вместе с MRR. Кроме того, объекты iOS никогда не имели функции сборки мусора. А с ARC также нет необходимости в сборке мусора в OS-X.

Вот простой пример ARC. Обратите внимание, что это не будет работать на онлайн-компиляторе, так как он не поддерживает ARC.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

Когда мы скомпилируем вышеуказанную программу, мы получим следующий вывод.

If you haven’t programmed for either OS X or iOS, you need to become acquainted with the primary programming language, Objective-C. Objective-C is a not a difficult language, and once you spend some time with it you’ll find it elegant as well. The Objective-C language enables sophisticated object-oriented programming. It extends the standard ANSI C programming language by providing syntax for defining classes and methods. It also promotes dynamic extension of classes and interfaces that any class can adopt.

If you are familiar with ANSI C, the following information should help you learn the basic syntax of Objective-C. And if you have programmed with other object-oriented languages, you’ll find that many of the traditional object-oriented concepts, such as encapsulation, inheritance, and polymorphism, are all present in Objective-C. If you are not familiar with ANSI C, it’s a good idea to at least read an overview of the C language before you attempt to read this article.

The Objective-C language is fully explained in The Objective-C Programming Language.

Objective-C is a Superset of the C Language

The Objective-C language specifies a syntax for defining classes and methods, for calling methods of objects, and for dynamically extending classes and creating programming interfaces adapted to address specific problems. As a superset of the C programming language, Objective-C supports the same basic syntax as C. You get all of the familiar elements, such as primitive types (int, float, and so on), structures, functions, pointers, and control-flow constructs such as if...else and for statements. You also have access to the standard C library routines, such as those declared in stdlib.h and stdio.h.

Objective-C adds the following syntax and features to ANSI C:

  • Definition of new classes

  • Class and instance methods

  • Method invocation (called messaging)

  • Declaration of properties (and automatic synthesizing of accessor methods from them)

  • Static and dynamic typing

  • Blocks—encapsulated segments of code that can be executed at any time

  • Extensions to the base language such as protocols and categories

Don’t worry if these aspects of Objective-C are unfamiliar to you now. As you progress through the remainder of this article, you will learn about them. If you’re a procedural programmer new to object-oriented concepts, it might help at first to think of an object as essentially a structure with functions associated with it. This notion is not too far off the reality, particularly in terms of runtime implementation.

In addition to providing most of the abstractions and mechanisms found in other object-oriented languages, Objective-C is a very dynamic language, and that dynamism is its greatest advantage. It is dynamic in that it permits an app’s behavior to be determined when it is running (that is, at runtime) rather than being fixed when the app is built. Thus the dynamism of Objective-C frees a program from constraints imposed when it’s compiled and linked; instead it shifts much of the responsibility for symbol resolution to runtime, when the user is in control.

Classes and Objects

As in most other object-oriented languages, classes in Objective-C support the encapsulation of data and define the actions that operate on that data. An object is a runtime instance of a class. It contains its own in-memory copy of the instance variables declared by its class and pointers to the methods of the class. You create an object in a two-step procedure called allocation and initialization.

The specification of a class in Objective-C requires two distinct pieces: the interface and the implementation. The interface portion contains the class declaration and defines the public interface of the class. As with C code, you define header files and source files to separate public declarations from the implementation details of your code. (You can put other declarations in your implementation file if they are part of the programmatic interfaces but are meant to be private.) These files have the filename extensions listed in the following table.

Extension

Source type

.h

Header files. Header files contain class, type, function, and constant declarations.

.m

Implementation files. A source file with this extension can contain both Objective-C and C code. It is sometimes called an source file.

.mm

Implementation files. A source file with this extension can contain C++ code in addition to Objective-C and C code. Use this extension only if you actually refer to C++ classes or features from your Objective-C code.

When you want to include header files in your source code, specify a pound import (#import) directive as one of the first lines in a header or source file; a #import directive is like C’s #include directive, except that it makes sure that the same file is never included more than once. If you want to import some or all of the header files of a framework, import the framework’s umbrella header file, which has the same name as the framework, and not individual header files. The syntax for importing the header files of the (hypothetical) Gizmo framework is:

The diagram below shows the syntax for declaring a class called MyClass, which inherits from the base (or root) class, NSObject. (A root class is one that all other classes directly or indirectly inherit from.) The class declaration begins with the @interface compiler directive and ends with the @end directive. Following the class name (and separated from it by a colon) is the name of the parent class. In Objective-C, a class can have only one parent.

A class declaration

You write declarations of properties and methods between the @interface and @end directives. These declarations form the public interface of the class. (Declared properties are described in “Declared Properties and Accessor Methods.”) A semicolon marks the end of each property and method declaration. If the class has custom functions, constants, or data types associated with its public interface, put their declarations outside the @interface . . . @end block.

The syntax for a class implementation is similar. It begins with the @implementation compiler directive (followed by the name of the class) and ends with the @end directive. Method implementations go in between. (Function implementations should go outside the @implementation…@end block.) An implementation should always import its interface file as one of the first lines of code.

#import "MyClass.h"
@implementation MyClass
 
- (id)initWithString:(NSString *)aName
{
    // code goes here
}
 
+ (MyClass *)myClassWithString:(NSString *)aName
{
    // code goes here
}
@end

Objective-C supports dynamic typing for variables containing objects, but it also supports static typing. Statically typed variables include the class name in the variable type declaration. Dynamically typed variables use the type id for the object instead. You find dynamically typed variables used in certain situations. For example, a collection object such as an array (where the exact types of the contained objects may be unknown) might use dynamically typed variables. Such variables provide tremendous flexibility and allow for greater dynamism in Objective-C programs.

This example shows statically and dynamically typed variable declarations:

MyClass *myObject1;  // Static typing
id       myObject2;  // Dynamic typing

Notice the asterisk (*) in the first declaration. In Objective-C, object references must always be pointers. If this requirement doesn’t make complete sense to you, don’t worry—you don’t have to be an expert with pointers to be able to start programming with Objective-C. You just have to remember to put the * in front of the variable names for statically typed object declarations. The id type implies a pointer.

Methods and Messaging

If you’re new to object-oriented programming, it might help to think of a method as a function that is scoped to a specific object. By sending a message to—or messaging—an object, you call a method of that object. There are two kinds of methods in Objective-C: instance methods and class methods.

  • An instance method is a method whose execution is scoped to a particular instance of the class. In other words, before you call an instance method, you must first create an instance of the class. Instance methods are the most common type of method.

  • A class method is a method whose execution is scoped to the method’s class. It does not require an instance of an object to be the receiver of a message.

The declaration of a method consists of the method type identifier, a return type, one or more signature keywords, and the parameter type and name information. Here’s the declaration of the insertObject:atIndex: instance method.

Method declaration syntax

For instance methods, the declaration is preceded by a minus sign (-); for class methods, the corresponding indicator is a plus sign (+). “Class Methods”, below, describes class methods in greater detail.

A method’s actual name (insertObject:atIndex:) is a concatenation of all of the signature keywords, including colon characters. The colon characters declare the presence of a parameter. In the above example, the method takes two parameters. If a method has no parameters, you omit the colon after the first (and only) signature keyword.

When you want to call a method, you do so by sending a message to the object that implements the method—or, in other words, by messaging that object. (Although the phrase «sending a message» is commonly used as a synonym for «calling a method,“ the Objective-C runtime does the actual sending.) A message is the method name along with the parameter information the method needs (properly conforming to type). All messages you send to an object are dispatched dynamically, thus facilitating the polymorphic behavior of Objective-C classes. (Polymorphism refers to the ability of different types of objects to respond to the same message.) Sometimes the method invoked is implemented by a superclass of the class of the object receiving the message.

To dispatch a message, the runtime requires a message expression. A message expression encloses with brackets ([ and ]) the message itself (along with any required parameters) and, just inside the leftmost bracket, the object receiving the message. For example, to send the insertObject:atIndex: message to an object held by the myArray variable, you would use the following syntax:

[myArray insertObject:anObject atIndex:0];

To avoid declaring numerous local variables to store temporary results, Objective-C lets you nest message expressions. The return value from each nested expression is used as a parameter or as the receiving object of another message. For example, you could replace any of the variables used in the previous example with messages to retrieve the values. Thus, if you had another object called myAppObject that had methods for accessing the array object and the object to insert into the array, you could write the preceding example to look something like the following:

[[myAppObject theArray] insertObject:[myAppObject objectToInsert] atIndex:0];

Objective-C also provides a dot-notation syntax for invoking accessor methods. Accessor methods get and set the state of an object, and thus are key to encapsulation, which is an important feature of all objects. Objects hide, or encapsulate, their state and present an interface common to all instances for accessing that state. Using dot-notation syntax, you could rewrite the previous example as:

[myAppObject.theArray insertObject:myAppObject.objectToInsert atIndex:0];

You can also use dot-notation syntax for assignment:

myAppObject.theArray = aNewArray;

This syntax is simply a different way to write [myAppObject setTheArray:aNewArray];. You cannot use a reference to a dynamically typed object (type of id) in a dot-notation expression.

You have used dot-notation syntax already for assigning to a variable in Your First Mac App:

float volume = self.track.volume;

Class Methods

Although the preceding examples sent messages to an instance of a class, you can also send messages to the class itself. (A class is an object of type Class created by the runtime.) When messaging a class, the method you specify must be defined as a class method instead of an instance method. Class methods are a feature similar to static class methods in C++.

You often use class methods as factory methods to create new instances of the class or for accessing some piece of shared information associated with the class. The syntax for a class method declaration is identical to that of an instance method except that you use a plus (+) sign instead of a minus sign for the method type identifier.

The following example illustrates how you use a class method as a factory method for a class. In this case, the array method is a class method on the NSArray class—and inherited by NSMutableArray—that allocates and initializes a new instance of the class and returns it to your code.

NSMutableArray *myArray = nil;  // nil is essentially the same as NULL
 
// Create a new array and assign it to the myArray variable.
myArray = [NSMutableArray array];

Declared Properties and Accessor Methods

A property in the general sense is some data encapsulated or stored by an object. It is either an attribute—such as a name or a color—or a relationship to one or more other objects. The class of an object defines an interface that enables users of its objects to get and set the values of encapsulated properties. The methods that perform these actions are called accessor methods.

There are two types of accessor methods, and each method must conform to a naming convention. A “getter” accessor method, which returns the value of a property, has the same name as the property. A «setter” accessor method, which sets a new value for a property, has the form setPropertyName:, where the first letter of the property name is capitalized. Properly named accessor methods are a critical element of several technologies of the Cocoa and Cocoa Touch frameworks, including key-value coding (KVC), which is a mechanism for accessing an object’s properties indirectly through their names.

Objective-C offers declared properties as a notational convenience for the declaration and implementation of accessor methods. In Your First Mac App you declared the volume property:

@property (assign) float volume;

Declared properties eliminate the need to implement a getter and setter method for each property exposed in the class. Instead, you specify the behavior you want using the property declaration. The compiler can then create—or synthesize—actual getter and setter methods based on that declaration. Declared properties reduce the amount of boilerplate code you have to write and, as a result, make your code much cleaner and less error prone. Use declared properties or accessor methods to get and set items of an object’s state.

You include property declarations with the method declarations in your class interface. You declare public properties in the class header files; you declare private properties in a class extension in the source file. (See “Protocols and Categories” for a short description of class extensions along with an example.) Properties of controller objects such as delegates and view controllers should typically be private.

The basic declaration for a property uses the @property compiler directive, followed by the type information and name of the property. You can also configure the property with custom options, which define how the accessor methods behave, whether the property is a weak reference, and whether it is read-only. The options are in parentheses following the @property directive.

The following lines of code illustrate a few more property declarations:

@property (copy) MyModelObject *theObject;  // Copy the object during assignment.
@property (readonly) NSView *rootView;      // Declare only a getter method.
@property (weak) id delegate;               // Declare delegate as a weak reference

The compiler automatically synthesizes declared properties. In synthesizing a property, it creates accessor methods for it as well as a private instance variable that “backs” the property. The instance variable has the same name as the property but with an underscore prefix (_). Your app should directly access an instance variable (instead of its property) only in methods for object initialization and deallocation.

If you want a different name for an instance variable, you can bypass autosynthesis and explicitly synthesize a property. Use the @synthesize compiler directive in the class implementation to ask the compiler to generate the accessor methods along with the specially named instance variable. For example:

@synthesize enabled = _isEnabled;

Incidentally, when you declare a property you can specify custom names for the accessor methods, typically to make the getter methods of Boolean properties follow a conventional form, as shown here:

@property (assign, getter=isEnabled) BOOL enabled; // Assign new value, change name of getter method

Blocks

Blocks are objects that encapsulate a unit of work—that is, a segment of code—that can be executed at any time. They are essentially portable and anonymous functions that one can pass in as parameters of methods and functions or that can be returned from methods and functions. Blocks themselves have a typed parameter list and may have an inferred or a declared return type. You can also assign a block to a variable and then call it just as you would a function.

A caret (^) is used as a syntactic marker for blocks. There are other, familiar syntactic conventions for parameters, return values, and body of the block (that is, the executed code). The following figure explicates the syntax, specifically when assigning a block to a variable.

image: ../Art/blocks.png

You can then call the block variable as if it were a function:

int result = myBlock(4); // result is 28

A block shares data in the local lexical scope. This characteristic of blocks is useful because if you implement a method and that method defines a block, the block has access to the local variables and parameters of the method (including stack variables) as well as to functions and global variables, including instance variables. This access is read-only, but if a variable is declared with the __block modifier, its value can be changed within the block. Even after the method or function enclosing a block has returned and its local scope has been destroyed, the local variables persist as part of the block object as long as there is a reference to the block.

As method or function parameters, blocks can serve as a callback. When invoked, the method or function performs some work and, at the appropriate moments, calls back to the invoking code—via the block—to request additional information or to obtain program-specific behavior from it. Blocks enable the caller to provide the callback code at the point of invocation. Instead of packaging the required data in a “context” structure, blocks capture data from the same lexical scope as the host method or function. Because the block code does not have to be implemented in a separate method or function, your implementation code can be simpler and easier to understand.

Objective-C frameworks have many methods with block parameters. For example, the NSNotificationCenter class of the Foundation framework declares the following method, which has a block parameter:

- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

This method adds an observer to the notification center (notifications are discussed in Streamline Your App with Design Patterns). When a notification of the specified name is posted, the block is invoked to handle the notification.

    opQ = [[NSOperationQueue alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"CustomOperationCompleted"
             object:nil queue:opQ
        usingBlock:^(NSNotification *notif) {
        // handle the notification
    }];

Protocols and Categories

A protocol declares methods that can be implemented by any class, even if those classes implementing the protocol don’t have a common superclass. Protocol methods define behavior that is independent of any particular class. Protocols simply define an interface that other classes are responsible for implementing. When your class implements the methods of a protocol, your class is said to conform to that protocol.

From a practical perspective, a protocol defines a list of methods that establishes a contract between objects without requiring them to be instances of any specific class. This contract enables communication between those objects. One object wants to tell another object about the events it’s encountering, or perhaps it wants to ask for advice about those events.

The UIApplication class implements the required behavior of an application. Instead of forcing you to subclass UIApplication to receive simple notifications about the current state of the application, the UIApplication class delivers those notifications by calling specific methods of its assigned delegate object. An object that implements the methods of the UIApplicationDelegate protocol can receive those notifications and provide an appropriate response.

You specify in the interface block that your class conforms to, or adopts, a protocol by putting the name of the protocol in angle brackets (<...>) after the name of the class from which it inherits. In Your First Mac App, adoption of the NSApplicationDelegate protocol is specified in the following line:

@interface TrackMixAppDelegate : NSObject <NSApplicationDelegate>

You do not have to declare the protocol methods you implement.

The declaration of a protocol looks similar to that of a class interface, with the exceptions that protocols do not have a parent class and they do not define instance variables (although they can declare properties). The following example shows a simple protocol declaration with one method:

@protocol MyProtocol
- (void)myProtocolMethod;
@end

For many delegate protocols, adopting a protocol is simply a matter of implementing the methods defined by that protocol. There are some protocols that require you to state explicitly that you support the protocol, and protocols can specify both required and optional methods.

When you begin exploring the header files of the Objective-C frameworks, you’ll soon encounter a line similar to this one:

@interface NSDate (NSDateCreation)

This line declares a category through the syntactical convention of enclosing the name of the category in parentheses. A category is a feature of the Objective-C language that enables you to extend the interface of a class without having to subclass it. The methods in the category become part of the class type (within the scope of your program) and are inherited by all the class’s subclasses. You can send a message to any instance of the class (or its subclasses) to invoke a method defined in the category.

You can use categories as a means for grouping related method declarations within a header file. You can even put different category declarations in different header files. The frameworks use these techniques throughout their header files for clarity.

You can also use an anonymous category known as a class extension to declare private properties and private methods in the implementation (.m) file. A class extension looks like a category except there is no text between the parentheses. For example, the following shows a typical class extension:

@interface TrackMixAppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSTextField *nameField;
@property (strong) MyDataObject *data;
@end

Defined Types and Coding Strategies

Objective-C has several terms that you should not use as the names of variables because they are reserved for special purposes. Some of these terms are compiler directives that are prefixed with an at sign (@, for example, @interface and @end). Other reserved terms are defined types and the literals that go with those types. Objective-C uses a number of defined types and literals that you won’t find in ANSI C. In some cases, these types and literals replace their ANSI C counterparts. The following table describes a few of the important ones, including the allowable literals for each type:

Type

Description and literal

id

The dynamic object type. The negative literal for both dynamically and statically typed objects is nil.

Class

The dynamic class type. Its negative literal is Nil.

SEL

The data type (typedef) of a selector; this data type represents a method signature at runtime. Its negative literal is NULL.

BOOL

A Boolean type. The literal values are YES and NO.

You often use these defined types and literals in error-checking and control-flow code. In your program’s control-flow statements, you can test the appropriate literal to determine how to proceed. For example:

NSDate *dateOfHire = [employee dateOfHire];
if (dateOfHire != nil) {
    // handle this case
}

To paraphrase this code, if the object representing the date of hire is not nil—in other words, it is a valid object—then the logic proceeds in a certain direction. Here’s a shorthand way of doing the same branching:

NSDate *dateOfHire = [employee dateOfHire];
if (dateOfHire) {
    // handle this case
}

You can even reduce these lines of code further (assuming you don’t need a reference to the dateOfHire object):

if ([employee dateOfHire]) {
    // handle this case
}

You handle Boolean values in much the same way. In this example, the isEqual: method returns a Boolean value:

BOOL equal = [objectA isEqual:objectB];
if (equal == YES) {
    // handle this case
}

You can shorten this code in the same way you can for the code that tests for the absence or presence of nil.

In Objective-C you can send a message to nil with no ill effects. Indeed there is no effect at all, except that the runtime returns nil if the method is supposed to return an object. Return values from messages sent to nil are guaranteed to work as long as what is returned is typed as an object.

Two other important reserved terms in Objective-C are self and super. The first term, self, is a local variable that you can use within a message implementation to refer to the current object; it is equivalent to this in C++. You can substitute the reserved word super for self, but only as the receiver in a message expression. If you send a message to self, the runtime first looks for the method implementation in the current object’s class; if it can’t find the method there, it looks for it in its superclass (and so on). If you send a message to super, the runtime first looks for the method implementation in the superclass.

The primary uses of both self and super have to do with sending messages. You send a message to self when the method to invoke is implemented by the class of self. For example:

self is also used in dot notation to invoke the accessor method synthesized by a declared property. For example:

NSString *theName = self.name;

You often send messages to super in overrides (that is, reimplementations) of methods inherited from a superclass. In this case, the method invoked has the same signature as the method overridden.

When, as a developer, you come from another language to Objective-C, you usually want to map the general concepts about programming you already know to this new language that at first might seems obscure. I remember myself being confused by the Objective-C syntax when I started learning it. What at first look might not make sense actually does a lot when you get a grasp of it and in my opinion (and the opinion of many other developers) makes the code much more readable. What at first sight might seem complicated is just something a bit different from what you are used to, thus feeling unfamiliar.

This guide is intended for developers who already know object oriented programming and goes over all the concepts you need to understand in order to use the language and learn programming for iOS and OS X.

It does not assume, though, that you know already C as other guides do. Many times, when people approach the language the first time, they get said to study C first. Although Objective-C is an object oriented extension built on top of C and inherits its the syntax, primitive types, and flow control statements, there are things you probably won’t ever use while writing Objective-C code or do it in very rare and specific occasions. So I believe that Objective-C and the parts you need from C can be learned along to each other. This guide integrates all the parts of C you need to know to start using the language, while ignoring the rest.

Table of contents

Comments
Variables and basic types
Operators
Object variables
A note on prefixes and namespaces
Branching and decisions
Relational and logical operators
Loops

Comments

Comments in Objective-C come directly from C and are similar to what you find in other languages. One line comments are written like this:

// This is a single line comment.

Everything after the // is ignored by the compiler until the end of the line. They also work after instructions:

int counter = 0; // This is a counter.

C allows also multiline comments, which some languages don’t have

/*
This is a multiline comment 
which spans more than one line.
*/

Variables and basic types

As in many other languages, you need to store your values and object inside variables. In some languages you can just use variables freely when you need them. Moreover you can change the content of the variable to anything you want: an integer, a decimal number, a string or an arbitrary object.

This does not happen in C. Variables are typed, which means that you have to declare the type of data a variable will contain and you won’t be able to change it later. Variable declarations in C look like this:

int variable1 = 7;
float variable2 = 3.14;
char variable3 = 'm';

Variable names can contain digits but cannot start with them and cannot include spaces, but can include capital letters (used for camel case) or the underscore character _

As you might have noticed in the example above, each instruction in C ends with a semicolon, which is required. Many languages do to, but some don’t, so this might be new for you. You will learn fast to end instructions with semicolons.

Take the habit of always initializing your variables to a specific value, even 0. In some languages variables are always initialized to 0 if no other value is specified. This does not happen in an Objective-C method, where non initialized variables will contain garbage and cause strange behaviours in your programs.

These are the most common types of C used in Objective-C programs.

Type            Description                             Examples

int             integer numbers, including negatives    0, 105, -12
unsigned int    positive integers                       0, 27, 315
float, double   floating point decimal numbers          1.61, -17.375
bool            boolean values                          true, false
char            single text character                   'a', 'F', '?'

If you need bigger numbers that the int type can contain, there are the long and the long long types, with the related unsigned types.

Now, the fact is that when you look at Objective-C code, you rarely find these basic types, which some developers and Apple SDKs still use, while you find others more. Here are some common ones you will find often:

NSInteger, for integer numbers
NSUInteger, for positive integers
CGFloat, for floating point decimal numbers, used for graphical values like drawing coordinates or sizes of graphical objects
BOOL, for booleans, which uses YES and NO instead of true and false

The reason these types where created was to make transition from 32 bits to 64 bits architectures easier. C types map to a specific sizes in memory, while the types above change regarding to the architecture the code is compiled for. Objective-C also has its own BOOL type (the reason for it is a bit more complicated).

Try to use the latter types when you can and pay attention to which types are used and returend by APIs, or you might end with weird results if values are truncated when switching between the two types.

C allows also to define other kind of simple types, a practice that is used extensively in Objective-C. We will see these types later.

Operators

Like any other language, C has its own arithmetic operators to work with the numeric values stored in the variables (which are probably the same as the ones you already know).

The arithmetic operators of C are the following:

Operator   Description                              Syntax

+          addition                                 a + b
-          subtraction                              a - b
*          multiplication                           a * b
/          division                                 a / b
%          module (integer remainder of division)   a % b

Arithmetic operators follow standard math operator precedence, so multiplication, division and modulo have precedence over addition and subtraction.

C has also increment and decrement operators:

Operator   Description   Syntax

++         increment     a++ or ++a
--         decrement     a-- or --a

Increment and decrement operators have precedence over the arithmetic operators. Pay particular attention to these operators. When used in isolation, they are equivalent:

someVariable++;

produces the same result as

++someVariable;

The difference comes out when you use them inside another expression. When put before the variable, they increment or decrement the value before it is used. When put after, they increments or decrement the value after using it.

This example explains how this works:

int  x;
int  y;

// Increment operators
x = 1;
y = ++x; // x is now 2, y is also 2
y = x++; // x is now 3, y is 2

// Decrement operators
x = 3;
y = x--; // x is now 2, y is 3
y = --x; // x is now 1, y is also 1

We have already seen the assignment operator = which might be different in the languages you are used to. That is not the only assignment operator. The arithmetic operators (with the exception of the modulo) attach to the assignment and produce another four assignment operators, which are used as a shorthand for incrementing, decrementing, multiplying and dividing the content of a variable:

Operator   Example    Shorthand for

+=         a += 10;   a = a + 10;
-=         a -= 7;    a = a - 7;
*=         a *= 3;    a = a * 3;
/=         a /= 5;    a = a / 5;

Unlike in other languages, in Objective-C there is no operator overloading. This means that these operators (and the other operators we will see later) only work with basic types and they do not work with objects (to concatenate strings, for example).

Object variables

Let’s have a first look at objects. Objective-C is an object oriented language after all, so we need variables to contain objects. The generic type for an object variable is id:

id myObject = nil;

The id type means that the variable can contain any type of object. The nil value is equivalent to what is called null in some other languages (and in C) and means the variable is empty and does not contain any object. Objective-C is a dynamically typed language, so we can declare variables of type id and use them for any object. We can then call any method we want on that object, regardless of the fact that that method might exist or not. This might be useful in some cases, but the majority of the time we want the compiler to check for our mistakes as soon as possible and give variables a defined type.

Object variables with a specific type are declared in a slightly different way:

NSString *aString = nil;

Now what does that * mean? Well, strictly speaking that means that this variable is a pointer, a thing that makes many programmers recoil in horror. Pointers are one of those things coming from C you don’t want to deal with. Many modern languages don’t have pointers, so this might be a concept you are not familiar with. Although in the future, when you will be more familiar with the language, it might be useful to know what a pointer is, this is something you don’t have to worry about for now. You can write Objective-C without knowing this concept yet.

When programming I rarely think in terms of pointers. Just remember this simple rule: you need a * when you declare an object variable and you don’t when you use it.

A note on prefixes and namespaces

As you probably noticed, all types in Objective-C have some prefix at the beginning of the name (NSString, CGFloat, etc). The reason is that Objective-C does not have namespaces. To avoid name collisions (different objects ending up having the same name) Apple decided to prepend a two letters prefix to its types and classes. The two letter are generally coming from the framework declaring the type: UIKit classes and types have a UI prefix (UIView, UILabel, etc.), Core Graphics uses the CG prefix (CGFloat, CGRect, etc.) and the Foundation and AppKit frameworks (the latter present only on the Mac) use NS (NSString, NSArray, etc.). Why NS? Because these libraries were first developed at NeXT for the NeXTSTEP operating system.

It is a good practice to you add a prefix to your classes and types too. Even if you are not developing a framework but a normal app (which is often the case), just use letters from the app name to be sure to avoid collisions to code you might add later from a different source (like some open source code you might use in your project). There is no strict check from the compiler, so you can omit it, but it’s better not to.

Branching and decisions

Let’s now see how flow control works in Objective-C. Flow control is another part that the language inherits directly from C, so the structures are the same. Decisions and branching happen, in Objective-C, through three branching statements.

If-else statement

The main statement to perform decisions in Objective-C is the if-else statement

if (expression) {
    if-body
} else {
    else-body
}

The if-body is executed only if the expression to YES, true or any non zero value. Otherwise the else-body is executed. The else clause can be omitted like in many other languages. In the case of just one instruction in the if-body or else-body, the respective curly braces can be omitted too. If you come from a language that has no curly braces, there are many different styles that state where to place them.

I personally use the K&R style now, that in my experience seems to be quite widespread in the Objective-C community, but I’ve moved through many different styles during my programming career. The only solid advice I can give you regarding this topic is to be consistent: pick one style and always use it everywhere.

If you want to use more than one condition, the if-else statement allows multiple branches. This is usually the case in many languages, although it’s not possible in a few.

if (expression1) {
    if-body
} else if (expression2) {
    else-if-body
} else {
    else-body
}

The else if clause can be repeated as many times as needed.

Switch statement

When you have multiple choices you can use, as we saw, the if-else statement with as many branches as needed. If the expression evaluates to an integer, though, C offers another branching facility, the switch statement

switch (expression) {
    case value1:
        case-body-1
        break;
    case value2:
        case-body-2
        break;
    ...
    default:
        default-body
        break;
}

The values for the different cases must be constant integers, which means that you can either write a number or a constant in them, but not a variable.

The break statements are actually used to exit immediately from the switch statement. They are not mandatory, but pay attention not to forget them, otherwise the execution will continue into the next case.

The default statement is optional, and is executed when the value of the expression does not match any case. The break in the default statement is not mandatory either and not necessary unless you put another case after the default, which I strongly advise against, since it would be a poor coding practice.

Ternary operator

Another branching facility that Objective-C inherits from C is the ternary operator, also known as conditional operator. This is an operator that in some languages might not be available, and it’s a shorthand of the if-else statement and a return statement (which we will see later, but you are probably already familiar with). The ternary operator takes this form:

condition ? expression1 : expression2;

If the condition evaluates to YES, true, or any non zero value,  expression1 is executed and it’s value is returned, otherwise the same happens for expression2. Thus, it is equivalent to the following:

if (condition)
    return expression1;
else
    return expression2;

The ternary operator has the advantage over the if-else statement that it can be written inside another expression, like an assignement. Thus this is possible and sometimes used:

a = condition ? expression1 : expression2;

Relational and logical operators

To write the conditions for the branching statement and the loops we will see in a short while, C and Objective-C offer the following relational operators:

Operator   Description                Syntax

==         equal to                   a == b
!=         not equal to               a != b
>          greater than               a > b
>=         greater than or equal to   a >= b
<          less than                  a < b
<=         less than or equal to      a <= b

Pay attention to the == operator. A very common mistake, especially when coming from other languages where this would not be allowed, is to use = in comparisons instead, which is the assignment operator. This is perfectly legal in C, as any assignment also returns a value that is checked by the if-else statement, which checks for 0 and non zero values. So, the program will compile and run perfectly, but probably not behave as you expect. This is a mistake that costs many people a lot of time in debugging. Luckily the latest versions of XCode might warn you of this common mistake when you build your project, if the proper settings are enabled.

To combine conditions and form compound ones, C offers the common logical operators you find in many other languages:

Operator   Description      Syntax

!          negation (not)   !a
&&         and              a && b
||         or               a || b

Pay attention to the double symbols used in and and or: C has also the single character versions, which are bitwise operators and not logical ones. We will have a look at these later.

Many languages have a strict boolean notion of true and false. As we have seen, C, and Objective-C, have a more loose notion where zero and non zero values are checked instead. Some old versions of C don’t even have the bool type. It’s certainly beneficial to think in terms of truthiness of the conditions when writing them. It’s also useful nonetheless to know a bit of what happens behind the curtains. Objective-C NO and YES values are also mapped respectively to 0 and 1.

This is useful to know for a very common idiom in Objective-C that comes from C. The nil value is also mapped to 0, which means that it is equivalent to NO. So, instead of checking if a variable (or value) is equal to nil, it’s common in Objective-C to check it’s negation, which is logically equivalent. So instead of writing

if (someObject == nil)

we write

 if (!someObject)

Many times, though, checking for nil values is not necessary as it is in other languages. We will see later why.

Loops

Objective-C loops also come from C. In addition to these Objective-C also introduces a statement to enumerate collections that we will see later when we will talk about those. Looping over collections is much more common in an Objective-C program, but the basic loops are still useful nonetheless.

For loop

The first loop statement, present in many modern languages as well is the for loop. In C it has the following syntax:

for (initialization-expression; loop-condition; update-expression) {
    loop-body
}

Please notice that the separators between initialization-expression, loop-condition and update-expression are semicolons and not commas.

The for loop is quite flexible and can accept different kind of expressions in it. In it’s most common form, it’s used like this:

The initialization-expression is an expression executed before entering the loop. It’s most commonly used to declare and/or initialize an integer variable used to count through the loop.

The loop-condition is a boolean condition used to check when to terminate the loop. It is evaluated before each pass through the loop. When the loop condition is false, the loop is terminated. This is usually used to check if the counter declared in the initialization-expression is over or under a certain value.

The update-expression is an expression that is evaluated at the end of each pass through the loop, after the loop body has been executed, and just before looping back to evaluate the condition-expression again. It’s usually used to update the value of the counter variable declared in the initialization-expression.

In it’s most common incarnation, the for loop takes the following form:

for (NSUInteger index = 0; index < someValue; index++) {
    loop-body
}

Where someValue is a variable or a constant with some positive integer value. In this way the loop iterates exactly someValue times.

While loop

The for-loop is normally used to iterate a fixed number of times. If we want to loop until a certain condition is met, then the while loop is normally used.

while (condition) {
    loop-body
}

The condition is checked at the beginning of every iteration and the loop ends when the condition evaluates to a false value. If it evaluates to false on the first iteration (before executing the loop-body once), then the loop-body is not executed.

Do-while loop

If we want the loop to iterate at least one time, we have the do-while loop.

do {
    loop-body
} while (condition);

In this case the order of loop-body execution and condition evaluation is reversed. First the loop goes through one iteration and then checks the condition. If it evaluates to true, the loop continues, until it evaluates to false.

As for the branching statement, the curly braces can be omitted in any loop if the loop body only has one instruction.

Break and continue

There are two statements that are used to alter the flow of the loop structures: break and continue. They can be used in any loop.

The break statement is used when you want to terminate the loop prematurely, for reasons often different from the loop condition and at any point during an iteration, so you only execute part of it. This illustrates how usually it’s used:

while (loop-condition) {
    some-instructions
    if (exit-condition)
        break;
    some-other-instructions
}

If the exit-condition evaluates to true during any iteration, the loop exits without executing some-other-instructions, and execution continues after the loop. You can of course use more than one break checks in the loop body.

Sometimes you want to just skip the rest of the current iteration like with the break statement, but without exiting the loop. The continue statement is used for this specific reason.

while (loop-condition) {
    some-instructions
    if (skip-condition)
        continue;
    some-other-instructions
}

When the skip-condition is true in any iteration, some-other-instructions are not executed, but the loop continues to the next iteration, checking again the loop-condition and executing the loop-body again.

The break and continue statements can also be mixed inside the same loop, to have a more complex control over the flow structure.

Matteo Manferdini

Matteo has been developing apps for iOS since 2008. He has been teaching iOS development best practices to hundreds of students since 2015 and he is the developer of Vulcan, a macOS app to generate SwiftUI code. Before that he was a freelance iOS developer for small and big clients, including TomTom, Squla, Siilo, and Layar. Matteo got a master’s degree in computer science and computational logic at the University of Turin. In his spare time he dances and teaches tango.

Table of Contents

  1. An Introductory Objective-C Xcode Command Line Application
  2. To do list app
  3. Default Commmand Line Application in Objective-C
  4. Default iOS Project in Objective-C
  5. iOS Objective-C Primer
  6. iOS Networking App in Objective-C — Using NSURLSession and NSJSONSerialization
  7. Challenges

Objective-C Fundamentals Primer for Swift Developers

Objective-C

Wikipedia: The programming language Objective-C was originally developed in the early 1980s. It was selected as the main language used by NeXT for its NeXTSTEP operating system, from which macOS and iOS are derived.

You will encounter the prefix NS used on types throughout Objective-C as a result of it being used at NeXTSTEP. A company that was started by Steve Jobs when he was ousted by Apple in the last 1980s. NeXTSTEP would be later acquired and Steve Jobs would rejoin Apple. The rest is history as we know it…..

Getting Starting with Objective-C as a Command-Line app

  • Open up Xcode
  • Navigate to File -> New -> Project
  • Select macOS Template and Command Line Tool
  • Name the Project
  • For Language, select Objective-C, then create.

When your Project has been created, navigate to main.m This main file will be used as a «Playground» for tesing the Objective-C code snippets. The main.m file should be similar to the snippet below

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    
    // code here
    NSLog(@"Hello, World");
    
  }
  return 0;
}

Run the Project and you should now see Hello, World printed in the console.

StarterProject Xcode Project

Printing to the Console

Objective-C

Swift

Some Primitive Types in Objective-C

  • int
  • float
  • double
  • BOOL

Format Specifiers

Format specifiers are used as placeholder tokens to String interpolate various data types.

%@ object such as NSString
%d int, NSInteger
%f float

// %@ example
NSString *wwdc2019 = @"SwiftUI";
NSLog(@"%@ was introduced at WWDC 2019", wwdc2019);
// SwiftUI was introduced at WWDC 2019

// %d example
int olympics = 2020;
NSLog(@"The next Summer Olympics will be held in %d", olympics);
// The next Summer Olympics will be held in 2020

// %f example
float currentIOSClass = 5.3;
NSLog(@"My current iOS class is %0.1f", currentIOSClass); // using %0.1f sets the float result to 1 decimal place
// My current iOS class is 5.3

NSString

NSString *message = @"Welcome to Objective-C"; // oops yeah, ; semicolons are required to end a statement in Obj-C
NSLog(@"%@", message);

// Welcome to Objective-C

NSMutableString — contents can be edited

NSMutableString *firstName = [[NSMutableString alloc] init];
[firstName appendString:@"Sal"];
NSLog(@"%@", firstName); // Sal
[firstName appendString:@"ly"]; // Sally
NSLog(@"%@", firstName);

In Objective-C a * pointer is used to signify an object. An object requires space on the heap, there this * pointer is a location on the heap. Primitives e.g int, float are not objects on the heap and do not have this * in their declaration.

NSArray

NSArray *programmingStacks = @[@"Swift", @"Objective-C", @"Python", @"C++", @"Java"];
NSLog(@"Currently learning %@", programmingStacks[1]);

// Currently learning Objective-C

NSMutableArray — adds modification operations

NSMutableArray *children = [[NSMutableArray alloc] init];
[children addObject:@"Miles"];
[children addObject:@"Norah"];
NSLog(@"%@", children);
/*
(
    Miles,
    Norah
)
*/

NSDictionary

NSDictionary *cites = @{@"Sweden": @"Stockholm", @"Japan": @"Tokyo", @"France": @"Paris"};
NSString *city = cites[@"Japan"];
NSLog(@"Capital city of Japan is %@", city);

// Capital city of Japan is Tokyo

NSMutableDictionary — adds modification operations

NSMutableDictionary *spokenLanguages = [[NSMutableDictionary alloc] init];
spokenLanguages[@"United States"] = @"English";
spokenLanguages[@"Columbia"] = @"Spanish";
NSLog(@"%@", spokenLanguages);
/*
 {
   Columbia = Spanish;
   "United States" = English;
 }
*/

Control Flow

if/else

BOOL isAwesome = NO; // all equivalent, YES, TRUE, true, NO, FALSE, false

if (isAwesome) { // parenthesis are REQUIRED in Obj-C unlike in Swift
  NSLog(@"Objective-C is awesome");
} else {
  NSLog(@"My ♥️ is with Swift");
}

// My ♥️ is with Swift

C-Style for loop

for (int i = 0; i <= 10; i++) {
  NSLog(@"%d", i);
}

/*
0
1
.
.
10
*/

for-in (fast-enumeration)

NSArray *pursuitClasses = @[@"android", @"iOS", @"web"];
for (NSString *pursuitClass in pursuitClasses) {
  NSLog(@"This is the %@ class", pursuitClass);
}

// This is the android class
// This is the iOS class
// This is the web class

Number values in Objective-C

Type Storage Size Value range
char 1 byte -128 to 127 or 0 to 255
unsigned char 1 byte 0 to 255
signed char 1 byte -128 to 127
int 2 or 4 bytes -32,768 to 32,767 or -2,147,483,648 to 2,147,483,647
unsigned int 2 or 4 bytes 0 to 65,535 or 0 to 4,294,967,295
short 2 bytes -32,768 to 32,767
unsigned short 2 bytes 0 to 65,535
long 4 bytes -2,147,483,648 to 2,147,483,647
unsigned long 4 bytes 0 to 4,294,967,295

Returning the maximum or minimum value of a scalar type

NSLog(@"INT_MAX is %d", INT_MAX); // 2147483647
NSLog(@"INT_MIN is %d", INT_MIN); // -2147483648

NSNumber

An object wrapper for primitive scalar numeric values.

Creating a NSNumber from a long

NSNumber *phoneNumber = [NSNumber numberWithLong: 9173451212];
NSLog(@"The phone number is %@", phoneNumber); // 9173451212

Some other number types in Objective-C

  • NSInteger
  • NSUInteger
  • NSDecimal
  • NSDecimalNumber
  • NSNumberFormatter

Functions and Methods

C-Style Functions

int add(int num1, int num2) {
  return num1 + num2;
}

// usage
int result = add(5, 4);
NSLog(@"result of adding numbers is: %d", result); // format specifier for int is %d

// result of adding numbers is: 9

Objective-C Methods (Functions)

Here we have an @interface declaration which acts as a template (later we will separate the @interface file in a .h file). Also we have an @implementation declaration that defines the function body (also we will be separating the @implementation file into a .m file)

We will continue to visit classes in Objective-C later, for now this snippet makes use of a class called ClassExample in this Function illustration.

@interface ClassExample: NSObject
- (NSString *) greeting:(NSString *)name;
@end

@implementation ClassExample
- (NSString *)greeting:(NSString *)name {
  NSString *newString = [NSString stringWithFormat:@"%@, hope you're enjoying all the [] bracket syntax in obj-c 😃", name];
  return newString;
}
@end

// usage 
ClassExample *classInstance = [[ClassExample alloc] init];
NSString *resultString = [classInstance greeting:@"Rachel"];
NSLog(@"Modified string is %@", resultString);

// Rachel, hope you're enjoying all the [] bracket syntax in obj-c 😃

Instance and Class methods in Objective-C

An instance method is denoted with a -(Return Type) prefix.

- (int)addNumber: (int)number1 toExistingNumber: (int) numberTwo; 

A class method is denoted with a +(Return Type) prefix.

Objective-C Functions with Multiple Parameters

Interface file.

@interface Person: NSObject 
- (void)contactAddress:(NSString *)address andPhoneNumber:(NSString *)phoneNumber;
@end 

Implementation file.

#import "Person.h"

@implementation Person: NSObject 
- (void)contactAddress:(NSString *)address andPhoneNumber:(NSString *)phoneNumber {
  NSLog(@"%@ address is %@ and phone number is %@", self.name, address, phoneNumber);
}
@end 

Test class.

Person *person = [[Person alloc] init];
person.name = @"Bob";

NSString *address = @"580 Broadway, New York";
NSString *phoneNumber = @"2127683422";

[person contactAddress:address andPhoneNumber:phoneNumber];
// Bob address is 580 Broadway, New York and phone number is 2127683422

Sending messages to a nil Object (does NOT crash in Objective-C which leads to logic errors and debugging time)

- (void) initializeProperites {
  // the _apiClient property needs to be allocated and initialized or using it will cause
  // methods on that class from getting called, compiler won't crash as in Swift when messages
  // are sent to nil objects
  if (_apiClient == nil) {
    _apiClient = [[AppleSearchAPI alloc] init];
  }
}

- (void)searchPodcast {
  
  // can send message to nil if _apiClient is nil, won't cause a compiler crash like in Swift
  // this causes logic errors as in such an example searchPodcast would not be called as expected
  // forgetting that Objective-C does allow messages to be sent to a nil causes unnecessary debugging time
  [self.apiClient searchPodcast];
}

Blocks in Objective-C (Closures in Swift)

Closures in Swift

func closureExample(num1: Int, num2: Int,
                    closure: (Int, Int) -> (Int)) -> Int {
  return closure(num1, num2)
}

let result = closureExample(num1: 10, num2: 5) { $0 * $1 }
print(result) // 50

Blocks in Objective-C is similar to Closures in Swift

This Block does not take any parameters and does not return a value.

void(^sampleBlock)(void);

sampleBlock = ^{
  NSLog(@"You may know me as a closure in Swift, however in Objective-C I'm called a Block");
};
    
sampleBlock();
// You may know me as a closure in Swift, however in Objective-C I'm called a Block

The Block below takes two parameters of type int and returns an int value.

int(^blockExample)(int, int);

blockExample = ^(int num1, int num2){
  return num1 + num2;
};
    
int result = blockExample(7, 21);
NSLog(@"result from block example calculation %d", result);

// result from block example calculation 28

Passing Blocks as Parameters in Functions

Header file

- (void)searchPodcast: (void (^)(NSError *, NSArray *))completionBlock;

Implementation file

- (void)searchPodcast: (void (^) (NSError *, NSArray *))completionBlock {  
  // code here
}

Enumerations

typedef NS_ENUM(NSInteger, NetworkError) {
  BadURL = 0,
  BadStatusCode = 1,
  APIError = 2
};

NetworkError networkError;
networkError = BadStatusCode;
switch (networkError) {
  case BadURL:
    NSLog(@"Bad URL");
    break;
  case BadStatusCode:
    NSLog(@"Bad Status Code");
    break;
  case APIError:
    NSLog(@"API Error");
    break;
}
// above if a break is not used at the end of each case it will act a a "fallthrough" in Swift

// Bad Status Code

C structure

typedef struct {
  CGFloat latitude;
  CGFloat longitude;
} Coordinate;


Coordinate coordinate;
coordinate.latitude = 40.6;
coordinate.longitude = 73.345;

NSLog(@"coordinate is %0.2f, %0.2f", coordinate.latitude, coordinate.longitude);

// coordinate is 40.60, 73.34

Classes

As opposed to Swift in Objective-C one file does not hold the full class (Model) implementation. A full class in Objective-C are separated into two files, a header file (.m extension) and an implementation file (.h extension).

The header file is interface (template) for the methods, initializers of the class. The body of the methods are not implemented in the interface file.

The implementations details and method body logic are implemented in the .m file.

Header file (.h Objective-C file extension)

@interface User : NSObject

// Properties
@property (copy) NSString *username;

// Methods: instance and class methods
// class methods are prefixed with a +
// instance is prefixed with a -
- (void)userInfo;

// Initializers

@end

Implementation file (.m Objective-C file extension)

@implementation User

- (void)userInfo {
  NSLog(@"Current logged user is %@", _username);
}

@end

ClassExample Xcode Project

Initializers

Header file

- (instancetype)initWithName: (NSString *)name; 

Implementation file

- (instancetype)initWithName:(NSString *)name {
  self.name = name;
  return self;
}

Create an instance of custom object

Person *alex = [[Person alloc] initWithName:@"Alex Paul"];

Properties

readonly — property will not be allowed to change through the setter property

@property (readonly) NSString *fullName;

readwrite — can read and write to this property — default property attribute, no need to specify this attribute

@property (readwrite) NSString *username;

copy — the copy attribute allows the property to keep a copy of itself so it’s not later mutated unintentionally

// in header file
@property (copy) NSString *firstName;

// usage in main.m  
NSMutableString *mutatingString = [[NSMutableString alloc] initWithString:@"@randomUser"];

user.username = mutatingString;


[user userInfo]; // Current logged user is @randomUser


[mutatingString appendString:@"Yoda"];

[user userInfo];

// if copy is not used on the username property
// output: Current logged user is @randomUserYoda

// if copy is used on the username property, it's original copy is maintained
// output: Current logged user is @randomUser
  1. Stackoverflow — Property Attributes Best Practices
  2. Apple — Property Attributes

Next Steps

Now that you have some Objective-C language fundamentals covered take a look at the Networking sample Xcode project in this repo. It uses NSURLSession along with NSJSONSerialization to query the Apple Search API for Podcasts. Project Link

Along the way to improving your iOS develeopment with Objective-C use this refresher for iOS specifics.

After you’ve gone through this repo go on to build your own Objective-C iOS project to improve on your skills and comfort with the language. :-] Happy Coding!!!!

Reading Resources

Apple — Programming with Objective-C
Apple — Swift Programming Language
Apple — Language Interoperability
Apple — Adopting Modern Objective-C
Apple — Conventions
Ray Wenderlich — Objective-C Style Guide
Google — Objective-C Style Guide

Понравилась статья? Поделить с друзьями:
  • Скачать руководство ремонта мотоцикл урал
  • Метилурацил побочные действия таблетки инструкция по применению
  • Часы skmei 1029 инструкция на русском
  • Пароочиститель homeclub gt 688 1 инструкция
  • Умный спрей приучение к туалету инструкция