Базовые трансформируемые атрибуты данных НЕ работают с NSPredicate
Я часто использую Transformable
за Core Data attributes
так что я могу изменить их позже.
Тем не менее, похоже, если я хочу использовать NSPredicate
найти NSManagedObject
, с помощью "uniqueKey == %@"
, или же "uniqueKey MATCHES[cd] %@"
работает не так, как должно.
Он всегда пропускает подходящие объекты, пока я не изменю атрибуты uniqueKey соответствующего объекта, чтобы иметь определенный класс, например NSString
, или же NSNumber
,
Может кто-нибудь объяснить ограничение использования NSPredicate
с Transformable
атрибуты?
3 ответа
Примечание: я не уверен, когда / если это изменилось с 5/2011 (из принятого ответа Скотта Ахтена), но вы можете абсолютно точно выполнить поиск с помощью NSPredicate по трансформируемым атрибутам. Скотт правильно объяснил, почему ваши предположения были нарушены, но может ли кто-нибудь объяснить ограничение использования NSPredicate с трансформируемыми атрибутами? был твой вопрос, он подразумевал, что это невозможно, а это неверно.
Так как это первый хит Google по запросу " Поиск и преобразование базовых данных nspredicate " (что я искал, пытаясь найти вдохновение), я хотел добавить свой рабочий ответ.
Как использовать NSPredicate с трансформируемыми свойствами
Короткий, опрометчивый ответ: вы должны быть умны в своих преобразователях данных. Вам нужно преобразовать значение в NSData, которое содержит то, что я назову "идентифицирующей примитив информацией", то есть наименьший, наиболее идентифицирующий набор байтов, который можно использовать для восстановления вашего объекта. Длинный ответ,...
Прежде всего, рассмотрим:
- Вы действительно хотели использовать трансформируемый атрибут? Если какой-либо поддерживаемый тип данных - даже двоичные данные - будет достаточно, используйте его.
- Вы понимаете, что на самом деле являются трансформируемыми атрибутами? Как они упаковывают и распаковывают данные в и из магазина? Просмотрите нестандартные постоянные атрибуты в документации Apple.
- После прочтения выше, спросите: работает ли пользовательский код, который скрывает поддерживаемый тип "атрибут поддержки"? Возможно, используйте эту технику.
Теперь, после этих соображений, трансформируемые атрибуты довольно гладкие. Честно говоря, написание NSValueTransformer "FooToData" для экземпляров Foo для NSData казалось более чистым, чем написание большого количества специального кода. Я не нашел случая, когда Core Data не знает, что ему нужно преобразовать данные, используя зарегистрированный NSValueTransformer.
Чтобы продолжить просто решить эти проблемы:
- Вы сказали Core Data, какой трансформатор использовать? Откройте базовую модель данных в табличном представлении, щелкните объект, щелкните атрибут, загрузите панель инспектора модели данных. В разделе "Тип атрибута: Преобразуемый" установите "Имя" для вашего преобразователя.
- Используйте преобразователь по умолчанию (опять же, смотрите предыдущие документы Apple) или напишите свой собственный преобразователь - transformedValue: должен возвращать NSData.
- NSKeyedUnarchiveFromDataTransformerName является преобразователем по умолчанию и может быть недостаточным или может рисовать в несколько преходящих данных экземпляра, которые могут сделать два одинаковых объекта разными, когда они равны.
- Преобразованное значение должно содержать только - что я назову - "примитивную идентификационную информацию". Магазин будет сравнивать байты, поэтому каждый байт считается.
- Вы также можете зарегистрировать свой трансформатор по всему миру. Я должен сделать это, так как я фактически использую их в другом месте приложения - например,
NSString *name = @"FooTrans"; [NSValueTransformer setValueTransformer:[NSClassFromString(name) new] forName:name];
Вы, вероятно, не хотите использовать операции преобразования данных с большим количеством запросов - например, большой импорт, где информация первичного ключа использует преобразователи - да!
И затем, в конце, я просто использую это, чтобы проверить на равенство для атрибутов объектов высокого уровня на моделях с 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];