Методы доступа управляемого объекта

Эта статья объясняет, почему Вы могли бы хотеть реализовать пользовательские методы доступа для управляемых объектов, и как реализовать их для атрибутов и для отношений. Это также иллюстрирует, как реализовать примитивные методы доступа.

Обзор

В OS X v10.5, Базовые Данные динамично генерируют эффективный общедоступный, и примитивный получают и устанавливают методы доступа атрибута и методы доступа отношения для классов управляемых объектов. Как правило, поэтому нет никакой потребности в Вас записать методы доступа для свойств, определяющихся в объекте соответствующей модели управляемого объекта управляемого объекта — несмотря на то, что можно использовать Objective C, объявил, что функция свойства объявила, что свойства подавляют предупреждения компилятора. Чтобы получить лучшую производительность — и получить преимущества от проверки типа — Вы используете методы доступа непосредственно, несмотря на то, что они - также кодирование значения ключа (KVC), совместимое поэтому, если необходимый можно использовать стандартные методы кодирования значения ключа такой как valueForKey:. Действительно необходимо записать пользовательские методы доступа при использовании переходных свойств для поддержки нестандартных типов данных (см. Нестандартные Персистентные Атрибуты), или если Вы используете скалярные переменные экземпляра для представления атрибута.

Пользовательская реализация

Реализация методов доступа Вы пишете для подклассов NSManagedObject обычно отличается от тех, Вы пишете для других классов.

  • Если Вы не обеспечиваете пользовательские переменные экземпляра, Вы получаете значения свойств от и сохраняете значения во внутреннее хранилище с помощью примитивных методов доступа.

  • Необходимо гарантировать, чтобы Вы вызвали соответствующий доступ и методы уведомления изменения (willAccessValueForKey:, didAccessValueForKey:, willChangeValueForKey:, didChangeValueForKey:, willChangeValueForKey:withSetMutation:usingObjects:, и didChangeValueForKey:withSetMutation:usingObjects:).

    NSManagedObject отключает автоматические уведомления изменения наблюдения значения ключа (KVO) для смоделированных свойств, и примитивные методы доступа не вызывают доступ и изменяют методы уведомления. Для несмоделированных свойств, на OS X v10.4 Базовые Данные также отключает автоматический KVO; на OS X v10.5 и позже, Базовые Данные принимают к NSObjectповедение.

  • В методах доступа для свойств, не определяющихся в модели объекта, можно или включить автоматические уведомления изменения или вызвать надлежащие методы уведомления изменения.

Можно использовать инструмент моделирования данных XCode для генерации кода для методов доступа для любого смоделированного свойства.

Значение ключа, кодирующее схему доступа

Использование кодирования значения ключа схемы доступа для управляемых объектов является в основном тем же как используемым для подклассов NSObject— посмотрите valueForKey:. Различие то, что, если после проверки нормальных разрешений valueForKey: выдал бы несвязанное ключевое исключение, механизм кодирования значения ключа для NSManagedObject проверки, является ли ключ смоделированным свойством. Если ключ соответствует свойство объекта, механизм сначала ищет метод доступа формы primitiveКлюч, и если это не найдено тогда, ищет значение для ключа во внутренней памяти управляемого объекта. Если они перестали работать, NSManagedObject выдает несвязанное ключевое исключение (точно так же, как valueForKey:).

Динамично сгенерированные методы доступа

По умолчанию Базовые Данные динамично создают эффективный общедоступный, и примитивный получают и устанавливают методы доступа для смоделированных свойств (атрибуты и отношения) классов управляемых объектов. Это включает значение ключа, кодирующее непостоянные методы прокси такой как add<Key>Object: и remove<Key>s:, как детализировано в документации для mutableSetValueForKey:— управляемые объекты являются эффективно непостоянными прокси для всех их к - много отношений.

Например, учитывая объект с атрибутом firstName, Базовые Данные автоматически генерируют firstName, setFirstName:, primitiveFirstName, и setPrimitiveFirstName:. Базовые Данные делают это даже для объектов, представленных NSManagedObject. Для подавления предупреждений компилятора при вызове этих методов необходимо использовать Objective C 2,0 заявленных функции свойств, как описано в Объявлении.

Данные Ядра методов доступа свойства генерируют, по умолчанию (nonatomic, strong)это - рекомендуемая конфигурация. Методы nonatomic потому что неатомарные средства доступа более эффективны, чем атомарные средства доступа, и в целом не возможно гарантировать потокобезопасность в Базовом Применении данных на уровне методов доступа. (Чтобы понять, как использовать Базовые Данные в многопоточной среде, посмотрите Параллелизм с Базовыми Данными.)

В дополнение к тому, чтобы всегда быть nonatomic, динамические свойства только соблюдают strong или copy атрибуты —weak обрабатывается как strong. Необходимо использовать copy экономно, поскольку это увеличивается наверху. Вы не можете использовать copy для отношений, потому что NSManagedObject не принимает NSCopying протокол, и это не важно поведению к - много отношений.

Объявление

Можно использовать Objective C, объявил, что функция свойства объявила свойства классов управляемых объектов — Вы обычно делаете это так, чтобы можно было использовать Данные Ядра средств доступа по умолчанию, обеспечивает, не генерируя предупреждения компилятора.

Вы объявляете атрибуты и отношения, поскольку Вы были бы свойства для любого другого объекта, как проиллюстрировано в следующем примере. Когда Вы объявляете к - многие отношение, тип свойства должен быть NSSet *. (Значение, возвращенное из получить средства доступа, не является KVO-совместимым непостоянным прокси — для большего количества подробных данных, посмотрите К - много отношений.)

@interface Employee : NSManagedObject
 
@property (nonatomic) NSString *firstName, *lastName;
@property (nonatomic) Department *department;
@property (nonatomic) Employee *manager;
@property (nonatomic) NSSet *directReports;
@end

Если Вы не используете пользовательский класс, для подавления предупреждений компилятора можно объявить свойства в категории NSManagedObject:

@interface NSManagedObject (EmployeeAccessors)
 
@property (nonatomic) NSString *firstName, *lastName;
@property (nonatomic) Department *department;
@property (nonatomic) Employee *manager;
@property (nonatomic) NSSet *directReports;
@end

Можно использовать те же методы для подавления предупреждений компилятора для автоматически сгенерированного к - много мутаторных методов отношения, например:

@interface Employee (DirectReportsAccessors)
 
- (void)addDirectReportsObject:(Employee *)value;
- (void)removeDirectReportsObject:(Employee *)value;
- (void)addDirectReports:(NSSet *)value;
- (void)removeDirectReports:(NSSet *)value;
 
@end

Для сохранения инкапсуляции, где класс атрибута имеет непостоянный подкласс и он реализует NSCopying протокол можно также использовать copy, например:

@property(nonatomic, copy) NSString* firstName, lastName;

Реализация

Можно указать реализацию с помощью @dynamic ключевое слово, как показано в следующем примере:

@dynamic firstName, lastName;
@dynamic department, manager;
@dynamic directReports;

Не должно обычно быть никакой потребности в Вас обеспечить Вашу собственную реализацию этих методов, если Вы не хотите поддерживать скалярные значения. Методы, которые Базовые Данные генерируют во время выполнения, более эффективны, чем те, можно реализовать себя.

Наследование

Если у Вас есть два подкласса NSManagedObject где родительский класс реализует динамическое свойство и его подкласс (внук NSManagedObject) переопределяет методы для свойства, те переопределения не могут вызвать super.

@interface Parent : NSManagedObject
@property(nonatomic, strong) NSString* parentString;
@end
 
@implementation Parent
@dynamic parentString;
@end
 
@interface Child : Parent
@end
 
@implementation Child
- (NSString *)parentString {
 
    // This throws a "selector not found" exception.
    return parentString.foo;
}
@end

Пользовательский атрибут и к - методы доступа отношения

Если Вы хотите реализовать свой собственный атрибут или к - методы доступа отношения, Вы используете примитивные методы доступа получить и установить значения от и до частного внутреннего хранилища управляемого объекта. Необходимо вызвать соответствующий доступ и методы уведомления изменения, как проиллюстрировано в Перечислении 1. NSManagedObjectреализация примитивного метода доступа набора обрабатывает управление памятью для Вас.

  Реализация перечисления 1 пользовательского класса управляемых объектов, иллюстрирующего методы доступа атрибута

@interface Department : NSManagedObject
 
@property(nonatomic, strong) NSString *name;
@end
 
@interface Department (PrimitiveAccessors)
- (NSString *)primitiveName;
- (void)setPrimitiveName:(NSString *)newName;
@end
 
 
@implementation Department
 
@dynamic name;
 
- (NSString *)name
{
    [self willAccessValueForKey:@"name"];
    NSString *myName = [self primitiveName];
    [self didAccessValueForKey:@"name"];
    return myName;
}
 
- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveName:newName];
    [self didChangeValueForKey:@"name"];
}
@end

Реализация по умолчанию не копирует значения атрибута. Если значение атрибута может быть непостоянным и реализует NSCopying протокол (как имеет место с NSString, например), можно скопировать значение в пользовательском средстве доступа, чтобы помочь сохранить инкапсуляцию (например, в случае, где экземпляр NSMutableString передается как значение). Это проиллюстрировано в Перечислении 2. Заметьте также, что (в целях иллюстрации) в этом примере получить средство доступа не реализовано — так как это не реализовано, Базовые Данные генерируют его автоматически.

  Реализация перечисления 2 пользовательского метода set копирования иллюстрирования класса управляемых объектов

@interface Department : NSManagedObject
{
}
@property(nonatomic, copy) NSString *name;
@end
 
@implementation Department
 
@dynamic name;
 
- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    // NSString implements NSCopying, so copy the attribute value
    NSString *newNameCopy = [newName copy];
    [self setPrimitiveName:newNameCopy];
    [self didChangeValueForKey:@"name"];
}
@end

Если Вы принимаете решение представлять атрибут с помощью скалярного типа (такой как NSInteger или CGFloat), или как одна из структур, поддерживаемых NSKeyValueCoding (NSRect, NSPoint, NSSize, NSRange), тогда необходимо реализовать методы доступа, как проиллюстрировано в Перечислении 3. Если Вы хотите использовать какой-либо другой тип атрибута, то необходимо использовать различный образец, описанный в Нестандартных Персистентных Атрибутах.

  Реализация перечисления 3 пользовательского класса управляемых объектов, иллюстрирующего скалярное значение атрибута

@interface Circle : NSManagedObject
@property CGFloat radius;
@end
 
@implementation Circle
{
    CGFloat _radius;
}
 
- (CGFloat)radius
{
    [self willAccessValueForKey:@"radius"];
    float f = _radius;
    [self didAccessValueForKey:@"radius"];
    return f;
}
 
- (void)setRadius:(CGFloat)newRadius
{
    [self willChangeValueForKey:@"radius"];
    _radius = newRadius;
    [self didChangeValueForKey:@"radius"];
}
@end

Пользовательский к - много методов доступа отношения

Вы обычно доступ к - много использований отношений mutableSetValueForKey:, который возвращает объект прокси, и видоизменяющий отношение и отправляющий надлежащие уведомления наблюдения значения ключа за Вами. Должно обычно быть мало причины реализовать Ваши собственные методы доступа набора для к - много отношений. Если они присутствуют, однако, платформа вызывает мутаторные методы (такой как add<Key>Object: и remove<Key>Object:) при изменении набора, представляющего персистентное отношение. (Выбранные свойства не поддерживают непостоянные методы доступа набора.) Для этого для работы правильно необходимо реализовать add<Key>Object:/remove<Key>Object: пара, add<Key>:/remove<Key>: пара или обе пары. Можно также реализовать другой получать средства доступа (такой как countOf<Key>:, enumeratorOf<Key>:, и memberOf<Key>:) и используйте их в своем собственном коде, однако их, как гарантируют, не вызовет платформа.

Если Вы действительно реализуете средства доступа набора для свойств модели, они должны вызвать соответствующие методы уведомления KVO. Перечисление 4 иллюстрирует реализацию методов доступа для к - многих отношение —employees— из класса Отдела. Самый простой способ генерировать реализацию состоит в том, чтобы выбрать отношение в инструменте моделирования XCode и выбрать Design> Data Model> Copy Obj-C 2.0 Метода {Объявления/Реализации} к Буферу обмена.

Перечисление 4  реализация иллюстрирования класса управляемых объектов пользовательских средств доступа для к - многие отношение

@interface Department : NSManagedObject
 
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSSet *employees;
@end
 
 
@interface Department (DirectReportsAccessors)
 
- (void)addEmployeesObject:(Employee *)value;
- (void)removeEmployeesObject:(Employee *)value;
- (void)addEmployees:(NSSet *)value;
- (void)removeEmployees:(NSSet *)value;
 
- (NSMutableSet*)primitiveEmployees;
- (void)setPrimitiveEmployees:(NSMutableSet*)value;
 
@end
 
 
@implementation Department
 
@dynamic name;
@dynamic employees;
 
- (void)addEmployeesObject:(Employee *)value {
 
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
 
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:changedObjects];
    [[self primitiveEmployees] addObject:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:changedObjects];
}
 
- (void)removeEmployeesObject:(Employee *)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
 
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:changedObjects];
    [[self primitiveEmployees] removeObject:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:changedObjects];
}
 
- (void)addEmployees:(NSSet *)value
{
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:value];
    [[self primitiveEmployees] unionSet:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:value];
}
 
- (void)removeEmployees:(NSSet *)value
{
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:value];
    [[self primitiveEmployees] minusSet:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:value];
}

Пользовательские примитивные методы доступа

Примитивные методы доступа подобны «нормальному» или общедоступному значению ключа, кодирующему совместимые методы доступа, за исключением того, что Базовые Данные используют их в качестве методов наиболее исходных данных для доступа к данным, следовательно они не выпускают доступ значения ключа или уведомления наблюдения. Другими словами они к primitiveValueForKey: и setPrimitiveValue:forKey: к чему общедоступные методы доступа valueForKey: и setValue:forKey:.

Обычно должно быть мало причины реализовать примитивные методы доступа. Если Вы хотите пользовательские методы обеспечить прямой доступ к переменным экземпляра для персистентных свойств Core Data, они, однако, полезны. Пример ниже контрастирует общедоступные и примитивные методы доступа для атрибута, int16, из типа Integer 16, сохраненный в пользовательской переменной экземпляра, nonCompliantKVCivar.

// primitive get accessor
- (short)primitiveInt16 {
    return nonCompliantKVCivar;
}
 
// primitive set accessor
- (void)setPrimitiveInt16:(short)newInt16 {
    nonCompliantKVCivar = newInt16;
}
 
// public get accessor
- (short)int16 {
    short tmpValue;
    [self willAccessValueForKey: @"int16"];
    tmpValue = nonCompliantKVCivar;
    [self didAccessValueForKey: @"int16"];
    return tmpValue;
}
 
// public set accessor
- (void)setInt16:(short)int16 {
    [self willChangeValueForKey: @"int16"];
    nonCompliantKVCivar = int16;
    [self didChangeValueForKey:@"int16"];
}