weezor
weezor

Reputation: 2631

NSValue/NSNumber creation of a given encoding/objCType

I'm trying to sync objects over GameCenter, accessing their values with KVC on both sides. Setting numeric values using setValue:forKey: requires them to be NSNumber objects.
NSValue initWithBytes:objCType: gives me NSValue objects even passing encodings for int, float and such.

Do you guys have a better solution instead of checking the encoding manually?

- (NSValue*)smartValueWithBytes:(void*)value objCType:(const char*)type
{
    if (0 == strcmp(type, @encode(int)))
    {
        int tmp;
        memcpy(&tmp, value, sizeof(tmp));
        return [NSNumber numberWithInt:tmp];
    }
    if (0 == strcmp(type, @encode(BOOL)))
    {
        BOOL tmp;
        memcpy(&tmp, value, sizeof(tmp));
        return [NSNumber numberWithBool:tmp];
    }
    //etc...
    return [NSValue valueWithBytes:value objCType:type];
}

If this is the way to go, is NSNumber the only NSValue subclass i need to take care of for KVC?

Upvotes: 2

Views: 3220

Answers (1)

Richard J. Ross III
Richard J. Ross III

Reputation: 55583

Here is my solution to the problem, only has specialization for floating-point values (seeing as they are weird!)

NSValue *safeValueForKVC(const void *input, const char *type)
{
    const char numericEncodings[] = { 
        'c',
        'i', 
        's', 
        'l', 
        'q', 
        'C', 
        'I',
        'S',
        'L',
        'Q',
        'f',
        'd',
    };
    const size_t sizeEncodings[] = {
        sizeof(char),
        sizeof(int),
        sizeof(short),
        sizeof(long),
        sizeof(long long),
        sizeof(unsigned char),
        sizeof(unsigned int),
        sizeof(unsigned short),
        sizeof(unsigned long),
        sizeof(unsigned long long),
        sizeof(float),
        sizeof(double),
    };

    int typeLen = strlen(type);

    if (typeLen == 1)
    {
        for (int i = 0; i < sizeof(numericEncodings); i++)
        {
            if (type[0] == numericEncodings[i])
            {
                // we have a numeric type, now do something with it
                if (i == 10)
                {
                    // floating-point value
                    float fValue = 0;

                    memcpy(&fValue, input, sizeEncodings[i]);

                    return [NSNumber numberWithFloat:fValue];
                }
                if (i == 11)
                {
                    // double value
                    double dValue = 0;

                    memcpy(&dValue, input, sizeEncodings[i]);

                    return [NSNumber numberWithDouble:dValue];
                }

                // standard numeric value, simply padding with false bits should work for any reasonable integer represetntation
                long long value = 0;
                memcpy(&value, input, sizeEncodings[i]);

                return [NSNumber numberWithLongLong:value];
            }
        }
    }

    return [[NSValue alloc] initWithBytes:input objCType:type];
}

Upvotes: 2

Related Questions