2009-08-02 3 views
12

У меня есть object_getInstanceVariable для работы как here, но, похоже, он работает только для поплавков, bools и ints не удваивается. Я подозреваю, что я делаю что-то не так, но я был в кругах с этим.object_getInstanceVariable работает для float, int, bool, но не для double?

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) также является правильным.

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

ответ

29

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

Решение состоит в использовании основного API, key-value coding, с использованием -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"]; 
+0

Так вот почему!Я потратил час, пытаясь понять, что происходит! Хорошее объяснение тоже. –

+0

Переменная obj никогда не определяется в методе, указанном выше, на самом деле должна быть self, correct? – wbyoung

+0

Э-э, да, вы абсолютно правы. –

2

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

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

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

+0

yup, который работает, не знал о valueForKey :, хотя мне все еще интересно, почему getInstanceVariable не работал. –

Смежные вопросы