Как сохранить CFBitVector (или любой CFType) в Core Data с помощью CFData/NSData?

У меня ограниченный опыт работы с типами и коллекциями Core Foundation, поэтому прошу прощения, если это очевидно.

Я использую тип CFBitVector для хранения некоторых битовых последовательностей, и мне нужно сохранить его в двоичном формате данных (чтобы его можно было добавить в хранилище основных данных). Наиболее разумным кажется хранить это в типе CFData, который можно бесплатно соединить с типом NSData и добавить в хранилище, но я не знаю, как это сделать.

Может ли кто-нибудь помочь мне с простым примером хранения CFTypes в CF/NSData?


Изменить:

Это вообще правильный подход? Должен ли я попытаться преобразовать CFBitVector в серию int, которые затем можно сохранить в модели данных? Или, может быть, трансформируемый атрибут?


person Stuart    schedule 08.07.2012    source источник


Ответы (1)


В итоге я сделал это, перевернув свой собственный преобразователь атрибутов, чтобы преобразовать экземпляр CFBitVectorRef в экземпляр NSData. Преимущество этого в том, что я действительно могу плотно втиснуть битовый массив в блок двоичных данных, так как в моем случае мне действительно нужно свести размер хранилища к минимуму.

Ниже приведена реализация моего класса CFBitVectorTransformer. По сути, он считывает каждый бит и упаковывает их в unsigned chars ("сегменты" в приведенном ниже коде), которые затем добавляются в изменяемый буфер NSData. Код будет работать с типами больше, чем unsigned chars, однако мне нужны были наименьшие возможные фрагменты, чтобы действительно минимизировать размер результирующих данных.

#define kBitsPerByte    8


@implementation CFBitVectorTransformer


+ (Class)transformedValueClass
{
    return [NSData class];
}

+ (BOOL)allowsReverseTransformation
{
    return YES;
}

/* CFBitVectorRef -> NSData */
- (id)transformedValue:(id)value
{
    if (!value) return nil;
    if ([value isKindOfClass:[NSData class]]) return value;

    /* Prepare the bit vector. */
    CFBitVectorRef bitVector = (__bridge CFBitVectorRef)value;
    CFIndex bitVectorCount = CFBitVectorGetCount(bitVector);

    /* Prepare the data buffer. */
    NSMutableData *bitData = [NSMutableData data];
    unsigned char bitVectorSegment = 0;
    NSUInteger bytesPerSegment = sizeof(char);
    NSUInteger bitsPerSegment = bytesPerSegment * kBitsPerByte;

    for (CFIndex bitIndex = 0; bitIndex < bitVectorCount; bitIndex++) {
        /* Shift the bit into the segment the appropriate number of places. */
        CFBit bit = CFBitVectorGetBitAtIndex(bitVector, bitIndex);
        int segmentShift = bitIndex % bitsPerSegment;
        bitVectorSegment |= bit << segmentShift;

        /* If this is the last bit we can squeeze into the segment, or it's the final bit, append the segment to the data buffer. */
        if (segmentShift == bitsPerSegment - 1 || bitIndex == bitVectorCount - 1) {
            [bitData appendBytes:&bitVectorSegment length:bytesPerSegment];
            bitVectorSegment = 0;
        }
    }

    return [NSData dataWithData:bitData];
}

/* NSData -> CFBitVectorRef */
- (id)reverseTransformedValue:(id)value
{
    if (!value) return NULL;
    if (![value isKindOfClass:[NSData class]]) return NULL;

    /* Prepare the data buffer. */
    NSData *bitData = (NSData *)value;
    char *bitVectorSegments = (char *)[bitData bytes];
    NSUInteger bitDataLength = [bitData length];

    /* Prepare the bit vector. */
    CFIndex bitVectorCapacity = bitDataLength * kBitsPerByte;
    CFMutableBitVectorRef bitVector = CFBitVectorCreateMutable(kCFAllocatorDefault, bitVectorCapacity);
    CFBitVectorSetCount(bitVector, bitVectorCapacity);

    for (NSUInteger byteIndex = 0; byteIndex < bitDataLength; byteIndex++) {
        unsigned char bitVectorSegment = bitVectorSegments[byteIndex];

        /* Store each bit of this byte in the bit vector. */
        for (NSUInteger bitIndex = 0; bitIndex < kBitsPerByte; bitIndex++) {
            CFBit bit = bitVectorSegment & 1 << bitIndex;
            CFIndex bitVectorBitIndex = (byteIndex * kBitsPerByte) + bitIndex;
            CFBitVectorSetBitAtIndex(bitVector, bitVectorBitIndex, bit);
        }
    }

    return (__bridge_transfer id)bitVector;
}


@end

Это хорошо абстрагирует преобразование данных, позволяя вам просто установить CFBitVectorRef в качестве атрибута в модели данных, и должно быть достаточно быстро для большинства целей.

Я надеюсь, что это поможет кому-то еще в подобной ситуации.

person Stuart    schedule 09.07.2012
comment
Я столкнулся с проблемой, что в - (id)reverseTransformedValue:(id)value, когда бит должен был быть равен 1, он, кажется, принимает значение 2,4,64,128 и т. д. Поэтому мне нужно вручную сделать CFBitVectorSetBitAtIndex(bitVector, bitVectorBitIndex, bit!=0). Не уверен, что это ошибка или я не правильно использовал коды. - person BabyPanda; 23.06.2015