object_getInstanceVariable работает для float, int, bool, но не для double?

StackOverflow https://stackoverflow.com/questions/1219081

Вопрос

У меня есть object_getInstanceVariable для работы в качестве здесь однако он работает только для чисел с плавающей запятой, bools и int, но не удваивается. Я подозреваю, что я делаю что-то не так, но я ходил кругами с этим.

float myFloatValue;
float someFloat = 2.123f;
object_getInstanceVariable(self, "someFloat", (void*)&myFloatValue);

работает, а myFloatValue = 2.123

но когда я пытаюсь

double myDoubleValue;
double someDouble = 2.123f;
object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);

Я получаю myDoubleValue = 0 . Если я пытаюсь установить myDoubleValue перед функцией, например. double myDoubleValue = 1.2f , значение не изменяется, когда я читаю его после вызова object_getInstanceVariable . Установка myIntValue на какое-то другое значение до того, как приведенная выше функция getinstancevar вернет 2, как и должно быть, т.е. это было изменено.

тогда я попробовал

Ivar tmpIvar = object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);

Если я выполню ivar_getName (tmpIvar) , я получу " someDouble " ;, но myDoubuleValue = 0 по-прежнему! Затем я пытаюсь ivar_getTypeEncoding (tmpIvar) и получаю " d " как и должно быть.

Итак, для подведения итогов, если typeEncoding = float , он работает, если он является двойным, результат не установлен, но он правильно читает переменную, и возвращаемое значение (Ivar) также является правильным.

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

Это было полезно?

Решение

object_getInstanceVariable - маленькая запутанная функция. Это задокументировано , что последний параметр является параметром void ** , то есть вы передаете адрес переменной void * и получаете указатель на переменная экземпляра, но она реализована так, как если бы это был параметр void * , то есть вы передаете адрес переменной, в которой вы хотите хранить копию переменной экземпляра. Проблема в том, что реализация игнорирует размер переменной экземпляра и просто копирует указатель. Так что все, что имеет такой же размер, как указатель, будет работать отлично Если вы работаете в 32-битной архитектуре, будут скопированы только старшие 32 бита. (Вы должны наблюдать такое же поведение с переменной экземпляра long long .)

Решением является использование основного API, кодирование значения ключа с использованием -valueForKey: .

Другое решение: если вы хотите написать фиксированную версию, скажем, как категорию для NSObject, она будет выглядеть примерно так:

@implementation NSObject (InstanceVariableForKey)

- (void *)instanceVariableForKey:(NSString *)aKey {
    if (aKey) {
        Ivar ivar = object_getInstanceVariable(self, [aKey UTF8String], NULL);
        if (ivar) {
            return (void *)((char *)self + ivar_getOffset(ivar));
        }
    }
    return NULL;
}

@end

Тогда ваш код будет выглядеть так:

double myDoubleValue = *(double *)[self instanceVariableForKey:@"someDouble"];

Другие советы

Как насчет использования valueForKey:?

NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(@"Double value: %f", [value doubleValue];

Примечание. Для этого необходимо иметь " someFloat " метод. Если вы хотите использовать setValue: forKey:, вам также понадобится " setSomeFloat: " метод. Это легко реализовать, объявив ivar как @property и синтезировав его.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top