Object_getInstanceVariable работает для float, int, bool, но не для double?
У меня object_getInstanceVariable
работает здесь, но, похоже, он работает только для float, bools и ints, а не для пар. Я подозреваю, что я делаю что-то не так, но я был в кругах с этим.
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) также корректно.
Я должен делать что-то основное, что я не могу видеть, поэтому я был бы признателен, если бы кто-нибудь мог указать на это.
Ответы
Ответ 1
object_getInstanceVariable
- путаница. documented, что последний параметр - это параметр 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"];
Ответ 2
Как насчет использования valueForKey:?
NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(@"Double value: %f", [value doubleValue];
Примечание. Для этого требуется, чтобы у вас был метод "someFloat". Если вы хотите использовать setValue: forKey:, вам также понадобится метод setSomeFloat:. Это легко реализовать, объявив ivar как @property и синтезируя его.