Статьи

Objective-C в сжатой форме: свойства

Теперь, когда мы изучили, какие типы данных доступны, мы можем поговорить о том, как на самом деле использовать их продуктивно. Мы узнали, как объявлять свойства в 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 .