Методы доступа управляемого объекта
Эта статья объясняет, почему Вы могли бы хотеть реализовать пользовательские методы доступа для управляемых объектов, и как реализовать их для атрибутов и для отношений. Это также иллюстрирует, как реализовать примитивные методы доступа.
Обзор
В 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"]; |
} |