object_getInstanceVariable работает для float, int, bool, но не для double?
-
10-07-2019 - |
Вопрос
У меня есть 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 и синтезировав его.