Атрибуты 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];