Как создавать классы и правильно пользоваться ими
Класс - это структура данных, у которой могут быть методы, переменные экземпляра и свойства, а также многие другие черты. Но пока мы не будем углубляться в подробности и поговорим об основах работы с классами. Каждый класс должен следовать таким правилам.
Класс должен наследовать от суперкласса. Из этого правила есть немногочисленные исключения. В частности, классы NSObject и NSProxy являются корневыми. У корневых классов не бывает суперкласса.
• Класс должен иметь имя, соответствующее Соглашению об именованиях методов в Cocoa.
• У класса должен быть файл интерфейса, в котором определяется интерфейс этого класса.
• У класса должна быть реализация, в которой вы прописываете все возможности, которые вы "обещали" предоставить согласно интерфейсу класса.
NSObject - это корневой класс, от которого наследуют практически все другие классы. В этом примере мы собираемся добавить класс под названием Person в проект, который был создан в подразделе "Создание и запуск вашего первого приложения для iOS" данного раздела. Далее мы добавим к этому классу два свойства, firstName и lastName, которые относятся к типу NSString. Выполните следующие шаги, чтобы создать класс Person и добавить его в ваш проект.
1. Откройте проект в Xcode и в меню File (Файл) выберите New-File (Новый- Файл).
2. Убедитесь, что слева, в разделе iOS, вы выбрали категорию Cocoa Touch. После этого выберите элемент Objective-C Class (Класс для Objective-C) и нажмите Next (Далее).
3. В разделе Class (Класс) введите имя Person.
4. В разделе Subclass of (Подкласс от) введите NSObject.
Когда справитесь с этим, нажмите кнопку Next (Далее). На данном этапе Xcode предложит вам сохранить этот файл. Просто сохраните новый класс в том каталоге, где находятся ваш проект и все его файлы. Это место выбирается по умолчанию. Затем нажмите кнопку Create (Создать) - и дело сделано.
После этого в ваш проект будут добавлены два новых файла: Person.h и Person.m. Первый файл - это интерфейс вашего класса Person, а второй - файл реализации этого класса. В Objective-C.h-файлы являются заголовочными. В таких файлах вы определяете интерфейс каждого файла. В.m-файле пишется сама реализация класса.
Теперь рассмотрим заголовочный файл нашего класса Person и определим для этого класса два свойства, имеющие тип NSString:
@interface Person: NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end
Как и переменные, свойства определяются в особом формате в следующем порядке.
1. Определение свойства должно начинаться с ключевого слова @property.
2. Затем следует указать квалификаторы свойства. Неатомарные (nonatomic) свойства не являются потокобезопасными. О безопасности потоков мы поговорим в главе 14. Вы можете указать и другие квалификаторы свойств: assign, copy, weak, strong или unsafe_unretained. Чуть позже мы подробнее поговорим и о них.
3. Затем укажите тип данных для свойства, например NSInteger или NSString.
4. Наконец, не забудьте задать имя для свойства. Имена свойств должны соответствовать рекомендациям Apple.
Как было указано ранее, свойства могут иметь различные квалификаторы. Вот важнейшие квалификаторы, в которых вы должны разбираться.
strong - свойства этого типа будут сохраняться во время исполнения. Они могут быть только экземплярами классов. Иными словами, вы не можете сохранить значение в свойстве типа strong, если значение является примитивом. Можно сохранять объекты, но не примитивы.
• copy - аналогичен strong, но при выполнении присваивания к свойствам этого типа среда времени исполнения будет делать копию объекта в правой части операции присваивания. Объект, находящийся в правой части этой операции, должен соответствовать протоколу NSCopying или NSMutableCopying.
• assign - значения объектов или примитивов, задаваемые в качестве значения свойства типа assign, не будут копироваться или сохраняться этим свойством. Для свойств примитивов этот квалификатор будет создавать адрес в памяти, в котором вы сможете поместить информацию примитива. В случае с объектами свойства такого типа будут просто указывать на объект в правой части равенства.
• unsafe_unretained - аналогичен квалификатору assign.
• weak - практически аналогичен квалификатору assign, но с одним большим отличием. При работе с объектами, когда объект, присвоенный свойству такого типа, высвобождается из памяти, среда времени исполнения будет автоматически устанавливать значение этого свойства в nil.
Итак, у нас есть класс Person с двумя свойствами, firstName и lastName. Вернемся к файлу реализации делегата нашего приложения (AppDelegate.m) и создадим объект типа Person:
#import "AppDelegate.h"
#import "Person.h"
@implementation AppDelegate
- (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
Person *person = [[Person alloc] init];
person.firstName = @"Steve";
person.lastName = @"Jobs";
self.window = [[UIWindow alloc]
initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
В этом примере мы выделяем и инициализируем наш экземпляр класса Person. Возможно, вы еще не понимаете, что это значит, но в подразделе "Добавление функционала к классам с помощью методов", приведенном далее, мы подробно об этом поговорим.
Добавление нового функционала к классам с помощью методов
Методы - это строительные блоки, из которых состоят классы. Например, класс Person может иметь логические возможности - обозначим их как "ходить", "дышать", "есть" и "пить". Обычно такие функции инкапсулируются в методах.
Метод может принимать параметры. Параметры - это переменные, передаваемые вызывающей стороной при вызове метода и видимые только этому методу. Например, в упрощенном мире у нашего класса Person был бы метод walk. Но вы могли бы добавить к этому методу параметр или аргумент и назвать его walkingSpeed. Этому параметру вы бы присвоили тип CGFloat. Теперь, если другой программист вызовет этот метод в вашем классе, он может указать, с какой скоростью будет идти Person. Вы как автор класса напишете соответствующий код, который будет обрабатывать различные скорости ходьбы Person. Не переживайте, если у вас возникает ощущение "как-то много работы получается". Рассмотрим следующий пример. В нем я добавил метод в файл реализации того класса Person, который мы создали в подразделе "Как создавать классы и правильно пользоваться ими" данного раздела.
#import "Person.h"
@implementation Person
- (void) walkAtKilometersPerHour:(CGFloat)paramSpeedKilometersPerHour{
/* здесь пишем код для этого метода */
}
- (void) runAt10KilometersPerHour{
/* Вызываем метод walk в нашем собственном классе и передаем значение 10 */
[self walkAtKilometersPerHour:10.0f];
}
@end
Типичный метод в языке Objective-C имеет следующие качества.
1. Префикс указывает компилятору, является ли данный код методом экземпляра (-) или методом класса (+). К методу экземпляра можно обратиться лишь после того, как программист выделит и инициализирует экземпляр вашего класса. Получить доступ к методу класса можно, вызвав его непосредственно из этого класса. Не волнуйтесь, если на первый взгляд это кажется сложным. В этой книге мы рассмотрим многочисленные примеры методов, пока просто следите за ходом рассказа.
2. Тип данных для метода, если метод возвращает какое-либо значение. В примере мы указали тип данных void. Так мы сообщаем компилятору, что не собираемся возвращать от метода какое-либо значение.
3. Первая часть имени метода, за которой идет первый параметр. Метод может и не иметь параметров. Методы, не принимающие параметров, довольно широко распространены.
4. Список последующих параметров, идущих за первым.
Рассмотрим пример метода с двумя параметрами:
- (void) singSong:(NSData *)paramSongData loudly:(BOOL)paramLoudly{
/* Параметры, к которым мы можем обратиться здесь, в этом методе, таковы:
paramSongData (для доступа к информации о песне)
paramLoudly сообщает нам, должны мы петь песню громко или нет
*/
}