Базовые трансформируемые атрибуты данных НЕ работают с NSPredicate - PullRequest
8 голосов
/ 24 мая 2011

Я часто использую Transformable для Core Data attributes, поэтому я могу изменить их позже.

Однако, похоже, если я хочу использовать NSPredicate, чтобы найти NSManagedObject, используя "uniqueKey == %@" или "uniqueKey MATCHES[cd] %@", это не будет работать должным образом.

Он всегда пропускает соответствующие объекты, пока я не изменю атрибуты uniqueKey соответствующего объекта, чтобы иметь определенный класс, например NSString или NSNumber.

Может кто-нибудь объяснить ограничение использования NSPredicate с Transformable атрибутами?

Ответы [ 3 ]

5 голосов
/ 16 февраля 2014

Примечание : Я не уверен, когда / если это изменилось с 5/2011 (из принятого ответа Скотта Ахтена), но вы можете абсолютно точно выполнить поиск с помощью NSPredicate по трансформируемым атрибутам. Скотт правильно объяснил, почему ваши предположения были нарушены, но если Может ли кто-нибудь объяснить ограничение использования NSPredicate с трансформируемыми атрибутами? был ваш вопрос, он подразумевал, что это невозможно, а это неправильно.

Так как это первый хит Google для " Core Data transform value search nspredicate " (что я искал, пытаясь найти вдохновение), я хотел добавить свой рабочий ответ.

Как использовать NSPredicate с трансформируемыми свойствами

Короткий, опрометчивый ответ: вы должны быть умны в своих преобразователях данных. Вам необходимо преобразовать значение в NSData, которое содержит то, что я назову «идентифицирующей примитив информацией», то есть наименьший, наиболее идентифицирующий набор байтов, который можно использовать для восстановления вашего объекта. Длинный ответ, ...

Прежде всего, рассмотрим:

  • Вы действительно хотели использовать трансформируемый атрибут? Если какого-либо поддерживаемого типа данных - даже двоичных данных - будет достаточно, используйте его.
  • Вы понимаете, что на самом деле являются трансформируемыми атрибутами? Как они упаковывают и распаковывают данные в и из магазина? Просмотрите нестандартные постоянные атрибуты в документации Apple.
    • После прочтения вышесказанного спросите: работает ли пользовательский код, который скрывает поддерживаемый тип "атрибута поддержки"? Возможно, используйте эту технику.

Теперь, после этих соображений, трансформируемые атрибуты довольно гладкие. Честно говоря, написание NSValueTransformer "FooToData" для экземпляров Foo для NSData казалось более чистым, чем написание большого количества специального кода. Я не нашел случая, когда Core Data не знает, что ему нужно преобразовать данные, используя зарегистрированный NSValueTransformer.

Чтобы продолжить, просто решите эти проблемы:

  • Вы сказали Core Data, какой трансформатор использовать? Откройте базовую модель данных в табличном представлении, щелкните объект, щелкните атрибут, загрузите панель инспектора модели данных. В разделе «Тип атрибута: преобразуемый» задайте «Имя» для вашего преобразователя.
  • Используйте преобразователь по умолчанию (опять же, смотрите предыдущие документы Apple) или напишите свой собственный преобразователь - transformedValue: must return 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
4 голосов
/ 24 мая 2011

Преобразуемые атрибуты обычно сохраняются в виде архивных двоичных данных.Таким образом, вы пытаетесь сравнить экземпляр NSData с экземпляром NSString или NSNumber.

Поскольку эти классы по-разному интерпретируют одни и те же данные, они не считаются совпадающими.

0 голосов
/ 24 июня 2012

вы можете попробовать таким образом

NSExpression *exprPath = [NSExpression expressionForKeyPath:@"transformable_field"];
NSExpression *exprKeyword = [NSExpression expressionForConstantValue:nsdataValue];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:exprPath rightExpression:exprKeyword modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
...