Первоначально Objective-C появился как настройка над C, в виде новых синтаксических конструкций заимствованных у Smalltalk, которые переводились препроцессором в обычные функции C. Кроме того библиотека времени выполнения обрабатывала такие вызовы. Так язык C имея в своем синтаксисе только структуру struct, становился обьектно ориентированным языком.
По сути Objective-C является и сейчас настройкой над C, потому что мы можем написать программу на чистом C, и передать компилятору Objective-C.
"Мир" Objective-C воспринимает как наличие обьектов, которые общаются друг с другом путем передачи сообщений - вызовом методов. Состояние обьектов определяются инвариантами и закрыты от внешнего мира, а поведение методами, которые принимают сообщения и могут измененять в результате состояние обьекта.
Файлы модулей *.m (есть применяется сметь Objective-C и C++, то *.mm)
Файлы заголовков *.h
Все создаваемые обьекты размещаются в динамической памяти, от сюда есть специальный тип id, который является под капотом void*, то есть указателем на обьект любого типа.
Чтобы узнать какого же типа текущий обьект NSObject - базовый класс для всех классов - приносит инвариант isa класса Class(позволяет узнавать имена своего и базового класса, а также инварианты класса и какие прототипы методов реализованые этим обьектом в нем).
Зарезервированные слова Objective-C, отличающиеся от слов C, начинаются с @.
Вывод в стандартный вывод
NSLog(@"Hello, World!");
Префикс NS от OS NeXTSTEP - это вроде образовательная операционная система над которой трудился Стив Джобс, когда его попросили из собственного Apple Computers, именно там развили Objective-C
Переменный и стандарный классы
Чтобы создать переменную типа строки мы должны использовать тип NSString*, это мы создаем ссылку на созданный обьект:NSString* firstName = @"World";
Как всегда в С-мире есть плейсхолдеры,
NSLog(@"Hello there, %@.", firstName);%@ - это плейсхолдер для любой переменной, которая передается вместе со строкой.
Есть тип числовой:
NSNumber* age = @28; NSLog(@"%@", age);
Массив, опять же нужно обратить внимание как мы создаем обьекты в памяти с помощью указания символом @, и потом ссылаемся на общую обвертку типом-ссылкой:
NSArray *apps = @[@"AngryFowl", @"Lettertouch", @"Tweetrobot"]; NSLog(@"%@", apps[1]);Так создаются неизменяемые массивы, но в Objective-C есть всегда изменяемый брат близнец: например NSMutableArray, NSMutableString
Мапа:
NSDictionary *appRatings = @{@"AngryFowl": @3, @"Lettertouch": @5};
NSLog(@"%@", appRatings[@"AngryFowl"]);
Вызов методов у обьектов
В Objective-C это называется "отправить сообщение", где сообщением называется имя метода[objectName messageName];
Мессидж, который присущий большинству встроенных обьектов это description, который возвращает строковое представление обьекта.
NSArray *foods = @[@"tacos", @"burgers"]; NSLog(@"%@", [foods description]);Вернет:
(
tacos,
burgers
)
У NSString есть мессидж length, возвращает он длину строки типа (NSUInteger)length, как видим тип у этого проперти без звезды;)
Чтоби воспользоваться логированием мы должны использовать другой плейсхолдер %lu:
NSUInteger cityLength = [city length]; NSLog(@"City has %lu characters", cityLength);
И так тут мы подошли к ключевому вопросу о звезде - Objective-C построен на C, поэтому он пользуется как собственными обьектами, так и обьектами C. Вот как их отличить:
CType var1;
ObCType* var2;
Тут и начинается мешанина, есть C-шные операторы, которые могут принимать только сишные обьекты, среди таких арифметические, поэтому сначала нужно сделать преобразование перед такими операциями:
NSNumber* higgiesAge = @6; NSNumber* phoneLives = @3; NSUInteger higgiesAgeInt = [higgiesAge unsignedIntegerValue]; NSUInteger phoneLivesInt = [phoneLives unsignedIntegerValue]; NSUInteger higgiesRealAge = higgiesAgeInt * phoneLivesInt; NSLog(@"Higgie is actually %lu years old.", higgiesRealAge);
Конкатинация строк происходит с помощью метода stringByAppendingString:
NSString* fullName = [[firstName stringByAppendingString:@" "] stringByAppendingString:lastName];
Замена подстроки:
NSString *replaced = [fullName stringByReplacingOccurrencesOfString:firstName withString:lastName];
Клонировние строк:
NSString *firstName = @"Andrii"; NSString *copy = [NSString stringWithString:firstName];
У стринга есть удобный метод для форматирования результирующей строки в сложной конкатинации
NSString *fullName = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
Простые фактори месиджи для создания обьектов и как создаются обьекты
Класси Objective-C имеют часто для создания инстанций месидж-фактори:
NSArray *emptyArray = [NSArray array]; NSDictionary *emptyDict = [NSDictionary dictionary];
NSString *emptyString = [[NSString alloc] init]; NSArray *emptyArray = [[NSArray alloc] init]; NSDictionary *emptyDictionary = [[NSDictionary alloc] init];Метод alloc запрашивает выделение в памяти под обьект, а уже init перетирает поточное состояние выделенного участка памяти пустым значением. Если обратиться к обьекту до init будет рантайм еррор. У стринга есть более осмысленный по названию инит с указанным значением:
NSString *copy = [[NSString alloc] initWithString:otherString];
Булинов тип
BOOL mrHiggieIsMean = YES;
if (mrHiggieIsMean) {
NSLog(@"Confirmed: he is super mean");
}
Алиас на множество значений указанного типа
typedef NS_ENUM(NSInteger, DayOfWeek) {
DayOfWeekMonday = 1,
DayOfWeekTuesday = 2,
DayOfWeekWednesday = 3,
DayOfWeekThursday = 4,
DayOfWeekFriday = 5,
DayOfWeekSaturday = 6,
DayOfWeekSunday = 7
};
Блоки кода
Что-то на подобии функций, которую мы можем определить, указать ей параметры для аргументов и потом многократно вызывать:
void (^myFirstBlock)(void) = ^{
NSLog(@"Hello from inside the block");
};
myFirstBlock();
А вот с аргументами:
void (^sumNumbers)(NSUInteger, NSUInteger) = ^(NSUInteger num1, NSUInteger num2){
NSLog(@"The sum of the numbers is %lu", num1 + num2);
};
sumNumbers(1, 2);
Чаще всегод это используется как лямбда выражение для передачи логики в какой-нибудь метод обьекта:
NSArray *newHats = @[@"Cowboy", @"Conductor", @"Baseball",
@"Beanie", @"Beret", @"Fez"];
[newHats enumerateObjectsUsingBlock:
^(NSString* hat, NSUInteger index, BOOL* stop){
NSLog(@"Trying on hat %@", hat);
}
];
Создание кастомных классов
Тут как в С++, сначало нам нужно определить хедер-файл, определяющий интерфейс TalkingiPhone.h:
А потом написать реализацию этого интрефейса TalkingiPhone.m::
Под капотом для каждого проперти создается сеттеры и геттеры, когда мы пользуемся вроде как обращением к проперти обьекта, на самом деле ме дергаем гетер/сетер.
@interface TalkingiPhone : NSObject @property NSString* phoneName; @property NSString* modelNumber;
- (NSString*) getSpeech;
- (void) speak; @end
А потом написать реализацию этого интрефейса TalkingiPhone.m::
#import "TalkingiPhone.h" @implementation TalkingiPhone
-(NSString*) getSpeech;
{
return [NSString stringWithFormat:@"%@ says \"Hello!\"", self.phoneName];
}
- (void) speak;
{
NSLog([self getSpeech]);
} @end
TalkingiPhone *phone = [[TalkingiPhone alloc] init]; phone.modelNumber = @"5s";Мы также можем создать поля только на чтение, но все равно есть способ их писать внутри класса, то есть мы убирает сетер для поля
Person.h:
@interface Person : NSObject @property NSString *firstName; @property (readonly) NSString *lastName; @endНо все же мы можем писать внутри так. Person.m:
#import "Person.h"
@implementation Person
- (void) changeLastName:(NSString *)newLastName;
{
_lastName = newLastName;
}
@end
Но есть и другой путь где про существование переменной снаружи нельза даже узнать, чтобы сделаеть ее приватной, мы ее делаем не пропертей, а переменной инстанции:Coffee.h:
@interface Coffee : NSObject {
NSNumber *_temperature;
}
@end
Здась подчеркивание в имени только неймконвеншин и ничего более. Но все же мы можем писать внутри так. Coffee.m:#import "Coffee.h"
@implementation Coffee
- (void)pour;
{
if([_temperature intValue] < 155){
NSLog(@"Oh no! The coffee is cold!");
} else {
NSLog(@"Mmmm, delicious warm coffee");
}
}
@end
Конструктор
#import "TalkingiPhone.h"
@implementation TalkingiPhone
- (TalkingiPhone *)init;
{
_batteryLife = @100;
return [super init];
}
Когда мы видим рантайм ексепшин NSInvalidArgumentException это означает, что мы передали сообщение обьекту, которое он не обрабатывает(вызвали метод, который не существует в классе)
Мы всегда можем узнать у обьекта умеет ли он обрабатывать указанное сообщение или нет:
if([talkingiPhone respondsToSelector:@selector(decreaseBatteryLife:)]){
NSLog(@"Yup, talkingiPhone responds to the decreaseBatteryLife: message");
}
Когда ми отправлем сообщение copy обьекту, то его класс должен реализовывать copyWithZone (которий означает, что клон обьекта будет сделан в том же участке памяти(зоне), где находится и первоначальный обьект - это делается для того, что обычно группы обьектов, исчисляются вместе, и более продуктивно с ними работать в одном месте памеяти - например удалить все вросто подряд в участке памяти, а не прыгать по разным участкам). Но насколько я понял зоны уже не имееют своей первоначальной реализации и смысла, а это осталось по традиции.
В Objective-C есть понятие протокола, в случае copyWithZone, реализацию которого требует метод copy базового обьекта NSObject. Часть протокола NSCopying Reference.
Чтобы класс стал реализовывать какой-нибудь протокол мы должны "пометить"(теггировать) его
//declaration @interface Person : NSObject@end //realization #import "Person.h" @implementation Person - (Person *) copyWithZone:(NSZone *)zone; { Person *personCopy = [[Person allocWithZone:zone] init]; return personCopy; } @end //usage Person *person = [[Person alloc] init]; Person *copy = [person copy];
Почти как конструктор, только катомный инициалайзер
Этот метод будет вызван на фазе инит, он должен обязательно начинаться с init в своем имениPerson.h:
@interface Person : NSObject
- (Person *) initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
@property NSString *firstName;
@property NSString *lastName;
@end
Person.m:
@implementation Person
- (Person *) initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
{
_firstName = firstName;
_lastName = lastName;
return [super init];
}
@end
И пользуемся:
Person *person = [[Person alloc] initWithFirstName:@"Tim"
lastName:@"Cook"];
Примеры реализации копиВизЗон c безопасной реализацией для наследников класса:
- (TalkingiPhone *) copyWithZone:(NSZone *)zone;
{
TalkingiPhone *copy = [[[self class] allocWithZone:zone] initWithBatteryLife:_batteryLife];
copy.phoneName = [NSString stringWithFormat:@"Copy of %@", self.phoneName];
return copy;
}
Комментариев нет:
Отправить комментарий