object_getInstanceVariable fonctionne pour float, int, bool, mais pas pour double?
-
10-07-2019 - |
Question
J'ai object_getInstanceVariable
qui fonctionne comme ici mais il semble que cela ne fonctionne que pour les floats, les bools et les ints, pas les doubles. Je soupçonne que je fais quelque chose de mal, mais je tourne en rond avec cela.
float myFloatValue;
float someFloat = 2.123f;
object_getInstanceVariable(self, "someFloat", (void*)&myFloatValue);
fonctionne et myFloatValue = 2.123
mais quand j'essaie
double myDoubleValue;
double someDouble = 2.123f;
object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
Je reçois myDoubleValue = 0
. Si je tente de définir myDoubleValue
avant la fonction, par exemple. double myDoubleValue = 1.2f
, la valeur n'est pas modifiée lorsque je la lis après l'appel object_getInstanceVariable
. Définir myIntValue
sur une autre valeur avant la fonction getinstancevar
ci-dessus renvoie 2 comme il se doit, c'est-à-dire. il a été changé.
alors j'ai essayé
Ivar tmpIvar = object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
Si je le fais ivar_getName (tmpIvar)
, j'obtiens "someDouble", mais myDoubuleValue = 0
quand même! Ensuite, j'essaie ivar_getTypeEncoding (tmpIvar)
et j'obtiens "d". comme il se doit.
Donc, pour résumer, si typeEncoding = float
, si cela est double, le résultat n'est pas défini, mais il lit correctement la variable et la valeur de retour (Ivar) est également correcte.
Je dois faire quelque chose de mal que je ne peux pas voir, alors j'apprécierais que quelqu'un me le signale.
La solution
object_getInstanceVariable
est une petite fonction confuse. C'est documenté que le dernier paramètre est un paramètre void **
& # 8212; en d'autres termes, vous transmettez l'adresse d'une variable void *
et obtenez un pointeur à la variable d'instance & # 8212; mais elle est implémentée comme s'il s'agissait d'un paramètre void *
& # 8212; c'est-à-dire que vous transmettez l'adresse de la variable sur laquelle vous souhaitez conserver une copie de la variable d'instance . Le problème est que l’implémentation ignore la taille de la variable d’instance et ne fait que copier le pointeur. Ainsi, tout ce qui a la même taille qu'un pointeur fonctionnera parfaitement. Si vous utilisez une architecture 32 bits, seuls les 32 bits les plus élevés seront copiés. (Vous devriez également observer le même comportement avec une variable d'instance long long
.)
La solution consiste à utiliser l'API principale, codage valeur-clé , à l'aide de -valueForKey:
.
L'autre solution: si vous vouliez écrire une version fixe, disons en tant que catégorie pour NSObject, cela ressemblerait à ceci:
@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
Ensuite, votre code ressemblerait à ceci:
double myDoubleValue = *(double *)[self instanceVariableForKey:@"someDouble"];
Autres conseils
Qu'en est-il de l'utilisation de valueForKey:?
NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(@"Double value: %f", [value doubleValue];
Remarque: pour cela, vous devez disposer d'un "someFloat". méthode. Si vous souhaitez utiliser setValue: forKey :, vous aurez également besoin de l'option " setSomeFloat: " méthode. Ceci est facilement implémenté en déclarant ivar comme propriété @ et en le synthétisant.