Атрибуты Core Data Transformable, не работающие с NSPredicate
Я часто использую Transformable
на Core Data attributes
, Так что я могу изменить их позже.
однако, похоже, если я хочу использовать NSPredicate
найти NSManagedObject
, используя "uniqueKey == %@"
или "uniqueKey MATCHES[cd] %@"
, он работает не так, как должен.
он всегда пропускает соответствующие объекты, пока я не изменю атрибуты uniqueKey соответствующего объекта, чтобы иметь определенный класс, такой как NSString
или NSNumber
.
может кто-нибудь объяснить ограничение использования NSPredicate
С Transformable
атрибуты?
3 ответов
Примечание: Я не уверен, когда/если это изменилось с 5/2011 (из принятого ответа Скотта Ахтена), но вы можете абсолютно искать с помощью NSPredicate по трансформируемым атрибутам. Скотт правильно объяснил, почему ваши предположения были нарушены, но если может ли кто-нибудь объяснить ограничение использования NSPredicate с трансформируемыми атрибутами? был ваш вопрос, он подразумевал, что это невозможно, и это неверно.
Так как это первый хит google за "Core Data transformable value search nspredicate" (то, что я искал, пытаясь найти вдохновение), я хотел добавить свой рабочий ответ.
как использовать NSPredicate с трансформируемыми свойствами
короткий, пьянящий ответ: вам нужно быть умным о ваших трансформаторах данных. Вам нужно перенести значение в NSData, которое содержит то, что я назову "примитивной идентифицирующей информацией", т. е. наименьший, самый идентифицирующий набор байтов, который может используется для реконструкции объекта. Длинный ответ. ,..
прежде всего, учитывать:
- вы действительно хотели использовать трансформируемый атрибут? Если какой-либо поддерживаемый тип данных-даже двоичные данные-будет достаточно, используйте его.
- вы понимаете, что такое трансформируемые атрибуты на самом деле? Как они упаковывают и распаковывают данные в магазин и из магазина? Обзор Нестандартные Постоянные Атрибуты в документации Apple.
- после прочтения выше, спросите: работает ли пользовательский код, который скрывает поддерживаемый тип "backing attribute"? Возможно, использовать эту технику.
Теперь, после этих соображений, трансформируемые атрибуты довольно скользкие. Честно говоря, писать NSValueTransformer "FooToData" для экземпляров Foo в NSData казалось чище, чем писать много пользовательского кода adhoc. Я не нашел случая, когда Core Data не знает, что ему нужно преобразовать данные с помощью зарегистрированного NSValueTransformer.
чтобы легко и просто решить эти проблемы:
- вы сказали Core Data, какой трансформатор использовать? Откройте модель основных данных в табличном представлении, щелкните сущность, щелкните атрибут, загрузите панель инспектор моделей данных. В разделе " тип атрибута: трансформируемый "установите" имя " для вашего трансформатора.
- используйте трансформатор по умолчанию (опять же, см. предыдущие документы Apple) или напишите свой собственный трансформатор -- transformedValue:должны возврат NSData.
- NSKeyedUnarchiveFromDataTransformername является трансформатором по умолчанию и может быть недостаточно, или может привлечь несколько переходных данных экземпляра, которые могут сделать два одинаковых объекта разными, когда они равны.
- преобразованное значение должно содержать только то, что я назову "примитивной идентифицирующей информацией". Хранилище будет сравнивать байты, поэтому каждый байт считается.
- вы также можете зарегистрироваться ваш трансформатор глобально. Я должен сделать это, так как я фактически использую их в другом месте приложения - например
NSString *name = @"FooTrans"; [NSValueTransformer setValueTransformer:[NSClassFromString(name) new] forName:name];
вы, вероятно, не хотите использовать преобразования сильно запрашиваемых операций с данными - например, большой импорт, где информация о первичном ключе использует трансформаторы - yikes!
и затем, в конце концов, я просто использую это для проверки равенства атрибутов объектов высокого уровня на моделях с NSPredicates - например, "%K = = % @ " - и он отлично работает. Я не пробовал различные совпадающие термины, но я бы не удивился, если бы они иногда работали, а другие нет.
вот пример преобразователя NSURL в NSData. Почему бы просто не сохранить строку? Да, это хорошо - это хороший пример пользовательского кода, маскирующего сохраненный атрибут. Этот пример иллюстрирует, что дополнительный байт добавляется к строковому URL для записи, если это был URL-адрес файла или нет-позволяя нам знать, какие конструкторы использовать, когда объект распакованный.
// URLToDataTransformer.h - interface
extern NSString *const kURLToDataTransformerName;
@interface URLToDataTransformer : NSValueTransformer
@end
...
// URLToDataTransformer.m - implementation
#import "URLToDataTransformer.h"
NSString *const kURLToDataTransformerName = @"URLToDataTransformer";
@implementation URLToDataTransformer
+ (Class)transformedValueClass { return [NSData class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value
{
if (![value isKindOfClass:[NSURL class]])
{
// Log error ...
return nil;
}
NSMutableData *data;
char fileType = 0;
if ([value isFileURL])
{
fileType = 1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value path] dataUsingEncoding:NSUTF8StringEncoding]];
}
else
{
fileType = -1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value absoluteString] dataUsingEncoding:NSUTF8StringEncoding]];
}
return data;
}
- (id)reverseTransformedValue:(id)value
{
if (![value isKindOfClass:[NSData class]])
{
// Log error ...
return nil;
}
NSURL *url = nil;
NSData *data = (NSData *)value;
char fileType = 0;
NSRange range = NSMakeRange(1, [data length]-1);
[data getBytes:&fileType length:1];
if (1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL fileURLWithPath:str];
}
else if (-1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:str];
}
else
{
// Log error ...
return nil;
}
return url;
}
@end
преобразуемые атрибуты обычно сохраняются как архивированные двоичные данные. Таким образом, вы пытаетесь сравнить экземпляр NSData с экземпляром NSString или NSNumber.
поскольку эти классы интерпретируют одни и те же данные по-разному, они не считаются совпадением.
вы можете попробовать этот способ
NSExpression *exprPath = [NSExpression expressionForKeyPath:@"transformable_field"];
NSExpression *exprKeyword = [NSExpression expressionForConstantValue:nsdataValue];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:exprPath rightExpression:exprKeyword modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];