
Переменные
Практически все переменные фактически есть экземплярами соответствующих классов.
Тип переменнных по-сути определяет класс. И, при создании экземпляра, перед его именем обычно указывают "*" (это означает что мы указываем на адрес в памяти нужного объекта).
Практически все значения экземпляров начинаются с "@".
Все классы в Objective-C унаследованы от NSObject, поэтому имеют общие методы (об этом далее)
Основные типы данных:
/***** простые типы *****/
BOOL someVar = YES;
BOOL otherVar = NO;
int i = 25;
char c = "f";
NSUInteger numInt = 25;
NSString *stringVar = @"Lorem ipsum";
NSNumber *num = @25;
/***** массив *****/
NSArray *cities = @[@"Kyiv", @"Odessa", @"Lviv"];
/***** ассоциативный массив *****/
NSDictionary *age = @{@"Alex": @26, @"Nick": @24};
Выводить в лог данные мы можем так:
NSLog(@"Some text"); //Выводим просто строку
NSLog(@"Variable: %@", stringVariable); //Выводим строку, заменяя строкой плейсхолдер %@
NSLog(@"Variable: %lu", intVariable); //Выводим строку, заменяя интеджер-переменной плейсхолдер %lu
NSLog(@"The person object is located at %p", person); //выводим адрес объекта person в памяти
Методы (отправка сообщений)
В Objective-C вызовом метода определенного объекта является отправка ему сообщения:
[someObject methodName];
Так как переменные есть объектами, то им тоже можно отправлять сообщения, например:
[someArray count]; //узнаем длину массива
[someArray description]; //возвращает отформатированную строку со всем содержанием переменной
Для передачи аргумента необходимо использовать двоеточие (каждый раз перед следующим аргументом). Здесь мы соединяем две строки:
[firstName stringByAppendingString:lastName];
В Objective-C счтается более правильно создавать объекты меттодом alloc, который выделяет необходимую память под их хранение:
NSString *emptyString = [[NSString alloc] init]; //создаем пустую строку
NSString *copy = [[NSString alloc] initWithString:otherString]; //создаем копию строки
Условия, итераторы
//сравнение NSString (возвращает BOOL):
[hat isEqualToString:@"Sombrero"]
//переключатели
switch (num) {
case 1:{
...
break;
}
case 2:{
...
break;
}
default: {
}
}
//цыкл, если тип массива NSDictionary, то word - ключ
for (NSString *word in funnyWords) {
NSLog(@"%@ is a funny word", word);
}
Функции
Блоки в Objective-C, они же функции, имеют весьма необычный синтаксис:
//без аргументов
void (^logMessage)(void) = ^{
NSLog(@"Hello from inside the block");
};
logMessage();
//с аргументом
void (^myFirstBlock)(NSString *) = ^(NSString *word){
NSLog(@"Hello %@ from inside the block", word);
};
myFirstBlock(@"World");
Интересна реализация аналогичной в php функции array_map:
//задаем функцию
void (^enumeratingBlock)(NSString *, NSUInteger, BOOL *) =
^(NSString *hat, NSUInteger index, BOOL *stop){
NSLog(@"Trying on hat %@", hat);
};
//делаем перебор элементов
[newHats enumerateObjectsUsingBlock:enumeratingBlock];
ОПП: классы, свойства, методы, объекты
Классы в Objective-C состоят из двух частей - хэдера (файл ClassName.h) и имплементации (реализации) (файл ClassName.m).
В хедере предопределяют свойства-переменные и методы (без их тела, т.е реализации). В имплементации - описание методов.
Хедер Person.h:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
-(void)speak; //предопределяем метод speak
@end
Имплементация Person.m:
#import "Person.h" //импортируем хедер
@implementation Person
-(void)speak;
{
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end
Методы класса имеют доступ к текущему экземпляру, который лежит по адресу self.
Свойства self.firstName, self.lastName досупны для чтения и записи везде.
Для того, чтобы запретить изменять свойства вне кода имплементации, достаточно в хедере написать:
@property (readonly) NSString *lastName;
Чтобы иметь возможность записать данные в такую переменную, в методе обращаемся к ней не "self.lastName", а "_lastName".
Существует способ задать свойство, который вовсе запрещает видеть его переменную где-либо кроме как внутри метода. Для этого в хедере:
@interface Person : NSObject {
NSNumber *_age;
}
@end
Методы могут возвращать данные и принимать аргументы:
//Person.h - добавляем аргумент greeting в методе speak
-(NSString *)speak:(NSString *)greeting;
//Person.m
-(NSString *)speak:(NSString *)greeting;
{
return [NSString stringWithFormat:@"%@ %@ say %@", _firstName, _lastName, greeting];
}
Для того, чтобы присвоить дефолтные значения экземпляру, делаем переопределение метода init, который унаследован от NSObject:
@implementation Person
- (Person *) init;
{
NSLog(@"Cool, a new Person is being initialized");
_firstName = @"Tim";
_lastName = @"Cook";
return [super init];
}
@end
Протоколы
Протоколы описывают методы, которые могут быть реализованы каким-либо классом, протокол включаем в хедер-файле.
В файле реализации класса необходимо реализовать все методы которые описывает протокол
@interface Person : NSObject <NSCopying>
@end
В данном случае протокол "NSCopying" описывает метод "copyWithZone", который позволяет отправлять сообщения "copy" и копировать объект в определенной зоне памяти
@implementation Person
- (Person *) copyWithZone:(NSZone *)zone;
{
Person *personCopy = [[[self class] allocWithZone:zone] init];
return personCopy;
}
@end
Для того, чтобы сделать скопировать и все свойства, необходимо инициализировать копию, для этого нужно заменить вызов "init" в методе "copyWithZone" на "initWithFirstName:_firstName", новый метод нужно, естественно описать в хедере и добавить его реализацию.
Так же, мы использовали [self class] чтобы обратится на прямую к классу копируемого объекта, т.к. наш исходный класс может быть наследован другим классом.
В случае с наследованиями мы можем не знать, какой тип указывать объекту, тогда мы можем писать так:
id person = [[Person alloc] init];
Здесь мы убрали "*" так как id уже есть указателем на объект. Далее, и метод "copyWithZone" должен возвращать тип "id" а не "Person"
В таком случае мы не сможем на прямую обращатся к свойствам
person.firstName = @"Eric"; //не будет работать
[person setFirstName:@"Eric"]; //будет работать