NSNumber et NSUInteger ne s'aiment pas (?)

TofTof Membre
septembre 2012 modifié dans API AppKit #1
Bonjour,



Si vous programmez sur Mac ou iOS vous avez sans doute déjà  utilisé NSNumber. Moi aussi et jusqu'à  présent je n'avais rien à  dire de particulier sur cette classe, jusqu'à  aujourd'hui.



Regardez ce bout de code :
NSUInteger  lUInteger = 100;<br />
NSNumber   *lNumber = [NSNumber numberWithUnsignedInteger:lUInteger];<br />
<br />
const char *lCtype = [lNumber objCType];<br />
<br />
// Test si valeur mémorisé comme un NSUInteger<br />
if (strcmp(lCtype, @encode(NSUInteger)) == 0)<br />
  NSLog(@&quot;OK&quot;);<br />
else<br />
  NSLog(@&quot;Error&quot;);<br />
<br />
// Test si valeur mémorisé comme un NSInteger<br />
if (strcmp(lCtype, @encode(NSInteger)) == 0)<br />
  NSLog(@&quot;OK&quot;);<br />
else<br />
  NSLog(@&quot;Error&quot;);<br />
<br />
//Output:<br />
//Error<br />
//Ok<br />




Quelqu'un pourrait il m'expliquer pourquoi NSNumber enregistre t il un NSUInteger comme un NSInteger ?



Merci pour vos réponses

Réponses

  • mpergandmpergand Membre
    septembre 2012 modifié #2
    Il semble que NSNumber ne fasse pas la différence, en fait il n'y en a pas, les bits sont les mêmes, c'est la façon de les interpréter qui change, c'est donc à  toi quand tu récupères cette valeur d'en faire un int ou un unsigned int.
  • TofTof Membre
    septembre 2012 modifié #3
    'mpergand' a écrit:


    Il semble que NSNumber ne fasse pas la différence, en fait il n'y en a pas, les bits sont les mêmes, c'est la façon de les interpréter qui change, c'est donc à  toi quand tu récupères cette valeur d'en faire un int ou un unsigned int.


    Ok alors pourquoi Apple propose t il la méthode [font=courier new,courier,monospace]numberWithUnsignedInteger[/font] dans ce cas si de toute manière ils transforment l'[font=courier new,courier,monospace]NSUInteger[/font] en [font=courier new,courier,monospace]NSInteger[/font] ?
  • AliGatorAliGator Membre, Modérateur
    Parce qu'Apple optimise ses classes (et c'est tant mieux).

    NSNumber est un class cluster. La façon qu'il a de stocker en interne les données ne doit pas importer, du moment que l'API façade est cohérente et fonctionne dans tous les cas.



    C'est pareil pour NSString d'ailleurs, un objet NSString peut encapsuler une chaà®ne Pascal, une chaà®ne C ASCII, un tableau de caractères unicodes (façon UTF-16), un tableau de chunks UTF-8... on s'en fiche un peu de comment il stocke ça en interne -- connaissant Apple ils ont optimisé le truc pour trouver le meilleur compromis entre taille de stockage et rapidité, selon aussi la façon dont tu initialises ta chaà®ne, etc).

    C'est pareil pour NSArray aussi. Un NSArray est parfois stocké en interne sous forme de tableau de pointeurs, comme un tableau C, parfois comme une liste chaà®née, selon le nombre d'éléments etc, mais on s'en fiche, c'est automatiquement optimisé.



    L'important ce n'est pas comment c'est foutu en interne, d'autant qu'Apple est très forte pour optimiser de ce côté (y'a qu'à  voir les articles comme celui de RidiculousFish qui explique les performances des classes container comme NSArray ou NSDictionnary quel que soit le nombre d'éléments, et l'adaptabilité de l'implémentation d'Apple), l'important est que ça fonctionne, fasse ce qu'on attend et respecte l'API façade, et ce de façon optimale.



    Pour en revenir à  NSNumber, j'ai testé ton code dans un projet iOS, et aucun de tes 2 tests ne passe quand j'essaye d'initialiser mon NSNumber avec 100. En l'occurrence, le objCType retourné est "q", autrement dit le type encoding pour "long long" (Alors que sous iOS NSUInteger est un "unsigned int"). Je pense qu'il a en interne une "union" pour les différents types primitifs vraiment différents, mais pour les entiers ils ont choisi de n'avoir qu'un storage de la taille maximum pour le type entier (soit "long long" sur une cible 64 bits comme iOS). Pour les types BOOL ils ont peut-être pris autre chose, pour les nombres flottants comme float et double certainement un storage commun de la taille d'un double, pour les types custom encore autre chose (certainement void*) -- n'oublions pas que NSNumber est une sous-classe de NSValue, dédiée pour manipuler les nombres, mais NSValue peut encapsuler n'importe quoi comme valeur primitive (cf sa méthode "initWithBytes:objCType:"). Mais bon a la limite c'est pas important. Le principal est qu'ils aient gardé tous les bits significatifs de la donnée à  stocker, pour pouvoir la récupérer sans perte à  tout moment



    D'ailleurs la doc de la méthode "objCType" de NSNumber le dit explicitement (paragraphe "Special Considerations"), le type Objective-C utilisé en interne par NSNumber ne correspond pas forcément à  la méthode avec laquelle le NSNumber a été initialisée. Car ce sont des considérations internes d'implémentation de NSValue et NSNumber (permettant certainement des optimisations sous le capot)
  • TofTof Membre
    septembre 2012 modifié #5
    Merci Ali pour les explications.

    PS: Le bout de code plus haut je l'ai testé sous Mac pas sous iOS



    Moi qui voulais pouvoir tester si, quand je recevais un NSNumber, la valeur passée étaient signée ou non signée je suis de la revue image/sad.png' class='bbc_emoticon' alt=':(' />



    Par curiosité j'ai fait quelque tests complémentaires. J'ai pris les différents type de nombre, regardé comment ils étaient encodés et ensuite comment NSNumber les stockés.



    Sous iOS (Simulateur) :
    char (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    unsigned char (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    short (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    unsigned short (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    int (32 bits): Value encode as [int], NSNumber encode as [int]<br />
    unsigned int (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    long (32 bits): Value encode as [long], NSNumber encode as [int]<br />
    unsigned long (32 bits): Value encode as [unsigned long], NSNumber encode as [long long]<br />
    long long (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    unsigned long long (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    float (32 bits): Value encode as [float], NSNumber encode as [float]<br />
    double (64 bits): Value encode as [double], NSNumber encode as [double]<br />
    BOOL (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    NSInteger (32 bits): Value encode as [int], NSNumber encode as [long long]<br />
    NSUInteger (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    SInt8 (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    UInt8 (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    SInt16 (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    UInt16 (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    SInt32 (32 bits): Value encode as [long], NSNumber encode as [int]<br />
    UInt32 (32 bits): Value encode as [unsigned long], NSNumber encode as [long long]<br />
    SInt64 (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    UInt64 (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]
    




    Sous iOS (Device, iPad1) :
    char (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    unsigned char (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    short (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    unsigned short (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    int (32 bits): Value encode as [int], NSNumber encode as [int]<br />
    unsigned int (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    long (32 bits): Value encode as [long], NSNumber encode as [int]<br />
    unsigned long (32 bits): Value encode as [unsigned long], NSNumber encode as [long long]<br />
    long long (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    unsigned long long (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    float (32 bits): Value encode as [float], NSNumber encode as [float]<br />
    double (64 bits): Value encode as [double], NSNumber encode as [double]<br />
    BOOL (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    NSInteger (32 bits): Value encode as [int], NSNumber encode as [long long]<br />
    NSUInteger (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    SInt8 (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    UInt8 (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    SInt16 (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    UInt16 (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    SInt32 (32 bits): Value encode as [long], NSNumber encode as [int]<br />
    UInt32 (32 bits): Value encode as [unsigned long], NSNumber encode as [long long]<br />
    SInt64 (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    UInt64 (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    




    Sous Mac OS X:
    char (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    unsigned char (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    short (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    unsigned short (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    int (32 bits): Value encode as [int], NSNumber encode as [int]<br />
    unsigned int (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    long (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    unsigned long (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    long long (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    unsigned long long (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    float (32 bits): Value encode as [float], NSNumber encode as [float]<br />
    double (64 bits): Value encode as [double], NSNumber encode as [double]<br />
    BOOL (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    NSInteger (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    NSUInteger (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]<br />
    SInt8 (8 bits): Value encode as [char], NSNumber encode as [char]<br />
    UInt8 (8 bits): Value encode as [unsigned char], NSNumber encode as [short]<br />
    SInt16 (16 bits): Value encode as [short], NSNumber encode as [short]<br />
    UInt16 (16 bits): Value encode as [unsigned short], NSNumber encode as [int]<br />
    SInt32 (32 bits): Value encode as [int], NSNumber encode as [int]<br />
    UInt32 (32 bits): Value encode as [unsigned int], NSNumber encode as [long long]<br />
    SInt64 (64 bits): Value encode as [long long], NSNumber encode as [long long]<br />
    UInt64 (64 bits): Value encode as [unsigned long long], NSNumber encode as [long long]
    




    Apple semble pas avoir spécialement optimisé NSNumber. Ils ont juste pas pris en compte le fait qu'ils soient signés ou pas.
Connectez-vous ou Inscrivez-vous pour répondre.