Теперь, когда мы изучили, какие типы данных доступны, мы можем поговорить о том, как на самом деле использовать их продуктивно. Мы узнали, как объявлять свойства в Hello, Objective-C, но эта глава углубляется в нюансы открытых свойств и переменных экземпляра. Сначала мы кратко рассмотрим базовый синтаксис свойств и переменных экземпляра, а затем обсудим, как использовать атрибуты поведения для изменения методов доступа.
Объявление недвижимости
Свойства могут быть объявлены в интерфейсе с помощью @property . В качестве краткого обзора давайте взглянем на файл Person.h который мы создали в главе Hello, Objective-C:
|
1
2
3
4
5
6
7
|
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (copy) NSString *name;
@end
|
Это объявляет свойство с именем name типа NSString . Атрибут (copy) сообщает среде выполнения, что делать, когда кто-то пытается установить значение name . В этом случае он создает независимую копию значения вместо указания на существующий объект. Мы поговорим об этом подробнее в следующей главе, Управление памятью.
Реализация свойств
В Hello, Objective-C мы использовали директиву @synthesize для автоматического создания методов получения и установки. Помните, что метод getter — это просто имя свойства, а метод setName по умолчанию — setName :
|
1
2
3
4
5
6
7
|
#import «Person.h»
@implementation Person
@synthesize name = _name;
@end
|
Но также возможно вручную создать методы доступа. Выполнение этого вручную помогает понять, что @property и @synthesize делают за кулисами.
Включенный пример кода: ManualProperty
Сначала добавьте новое свойство в интерфейс Person :
|
1
2
|
@property (copy) NSString *name;
@property unsigned int age;
|
Обратите внимание, что мы сохраняем age как примитивный тип данных (не указатель на объект), поэтому ему не требуется звездочка перед именем свойства. Вернувшись в Person.m , определите методы доступа явно:
|
1
2
3
4
5
6
7
|
— (unsigned int)age {
return _age;
}
— (void)setAge:(unsigned int)age {
_age = age;
}
|
Это именно то, что @synthesize сделал бы для нас, но теперь у нас есть возможность проверить значения до их назначения. Однако нам не хватает одной вещи: переменной экземпляра _age . @synthesize автоматически создал _name ivar, что позволяет нам отказаться от свойства name .
Переменные экземпляра
Переменные экземпляра, также известные как ivars, являются переменными, предназначенными для использования внутри класса. Они могут быть объявлены внутри фигурных скобок после директив @implementation или @implementation . Например, в Person.h измените объявление интерфейса на следующее:
|
1
2
3
|
@interface Person {
unsigned int _age;
}
|
Это определяет переменную экземпляра с именем _age , поэтому этот класс теперь должен успешно скомпилироваться. По умолчанию переменные экземпляра, объявленные в интерфейсе, защищены . Эквивалентное определение класса C # будет примерно таким:
|
1
2
3
|
class Person {
protected uint _age;
}
|
Модификаторы области Objective-C такие же, как и в C #: закрытые переменные доступны только для содержащего класса, защищенные переменные доступны для всех подклассов, а открытые переменные доступны для других объектов. Вы можете определить область действия переменных экземпляра с помощью @private , @protected и @public внутри @interface , как показано в следующем коде:
|
1
2
3
4
5
6
7
8
|
@interface Person : NSObject {
@private
NSString *_ssn;
@protected
unsigned int _age;
@public
NSString *job;
}
|
Публичные ивары на самом деле немного за пределами норм Objective-C. Класс с открытыми переменными действует скорее как структура C, чем как класс; вместо обычного синтаксиса обмена сообщениями вам нужно использовать оператор указателя -> . Например:
|
1
2
3
4
|
Person *frank = [[Person alloc] init];
frank->job = @»Astronaut»;
NSLog(@»%@», frank->job);
// NOT: [frank job];
|
Однако в большинстве случаев вы захотите скрыть детали реализации, используя декларацию @property вместо общедоступных переменных экземпляра. Кроме того, поскольку переменные экземпляра являются технически подробностями реализации, многие программисты предпочитают сохранять все переменные экземпляра закрытыми. С учетом этого, ивары, объявленные в @implementation , по умолчанию являются закрытыми. Итак, если бы вы переместили объявление Person.m в Person.m вместо заголовка:
|
1
2
3
|
@implementation Person {
unsigned int _age;
}
|
_age будет определен как закрытая переменная. Помните об этом при работе с переменными экземпляра в подклассах, так как разные значения по умолчанию для интерфейса и декларации реализации могут сбивать с толку новичков в Objective-C.
Настройка аксессоров
Но хватит о переменных экземпляра; давайте вернемся к свойствам. Методы доступа могут быть настроены с использованием нескольких атрибутов объявления свойств (например, (copy) ). Некоторые из наиболее важных атрибутов:
-
getter=getterName— Настройте имя метода доступа геттера. Помните, что по умолчанию это просто имя свойства. -
setter=setterName— настроить имя метода доступа метода установки. Помните, чтоsetзначение по умолчанию, за которым следует имя свойства (например,setName). -
readonly— сделать свойство доступным только для чтения, то есть будет синтезирован только геттер. По умолчанию свойства доступны для чтения и записи. Это нельзя использовать с атрибутомsetter. -
nonatomic— указывает, что методы доступа не должны быть потокобезопасными. По умолчанию свойства являются атомарными, что означает, что Objective-C будет использовать блокировку / сохранение (описанную в следующей главе), чтобы вернуть полное значение из метода получения / установки. Обратите внимание, однако, что это не гарантирует целостность данных между потоками — просто, что методы получения и установки будут атомарными. Если вы не в многопоточной среде, неатомарные свойства намного быстрее.
Распространенным вариантом использования для настройки имен получателей является соглашение о булевых именах. Многим программистам нравится добавлять в булевы имена переменных. Это легко реализовать с помощью атрибута getter :
|
1
|
@property (getter=isEmployed) BOOL employed;
|
Внутри класс может использовать employed переменную, но другие объекты могут использовать isEmployed setEmployed isEmployed и setEmployed для взаимодействия с объектом:
|
1
2
3
4
5
6
7
8
|
Person *frank = [[Person alloc] init];
[frank setName:@»Frank»];
[frank setEmployed:YES];
if ([frank isEmployed]) {
NSLog(@»Frank is employed»);
} else {
NSLog(@»Frank is unemployed»);
}
|
Многие другие атрибуты свойств относятся к управлению памятью, что будет обсуждаться в следующем разделе. Также возможно применить несколько атрибутов к одному свойству, разделяя их запятыми:
|
1
|
@property (getter=isEmployed, readonly) BOOL employed;
|
Синтаксис точек
В дополнение к методам получения / установки, также возможно использование точечной нотации для доступа к объявленным свойствам. Для разработчиков на C # это должно быть гораздо более знакомым, чем синтаксис обмена сообщениями в Objective-C в квадратных скобках:
|
1
2
3
|
Person *frank = [[Person alloc] init];
frank.name = @»Frank»;
NSLog(@»%@», frank.name);
|
Обратите внимание, что это просто удобство — оно переводится непосредственно в методы получения / установки, описанные ранее. Точечная запись не может использоваться для методов экземпляра.
Резюме
Свойства являются неотъемлемой частью любого объектно-ориентированного языка программирования. Это данные, на которых работают методы. Директива @property — это удобный способ настроить поведение свойства, но она не делает ничего, что нельзя сделать, вручную создав методы getter и setter.
В следующей главе мы подробно рассмотрим, как свойства хранятся в памяти, а также несколько новых атрибутов свойств для управления этим поведением. После этого мы углубимся в методы, которые завершают основные объектно-ориентированные инструменты Objective-C.
Этот урок представляет собой главу из Objective-C, лаконично , бесплатную электронную книгу от команды Syncfusion .