12
votes

Résoudre le problème de Nsinteger <-> Nsnumber

J'ai écrit une grande application iPhone de réseautage social et l'une des plus grandes problèmes que je rencontre est le fait que Nsinteger (et tous les autres types de NS-non d'objet) ne sont pas des citoyens de première classe. Ce problème découle du fait que, évidemment, ils n'ont aucune représentation pour une valeur nulle.

Cela crée deux problèmes principaux:

  1. des tonnes de frais généraux et d'opacité à convertir vers et de NSNumber lors de la conservation / de la récupération d'une collection.
  2. ne peut pas représenter nil. Souvent, je veux être capable de représenter une valeur "non définie".

    Un moyen de résoudre ce problème est d'utiliser NSNumber tout le temps, mais cela devient extrêmement déroutant. Dans un objet modèle d'utilisateur, j'aurais environ 20 nsnumbers différents et aucun moyen facile de dire si chacun est un flotteur, entier, Bool, etc.

    Voici donc mes pensées pour les solutions potentielles et les avantages / inconvénients. Je ne suis vraiment vendu sur aucun d'entre eux, alors je pensais demander des commentaires et / ou des solutions alternatives à ce problème.

    1. Continuez à utiliser des types de Nsinteger et utilisez simplement Nsintgermax pour représenter nul.
      PRO-THÈME DE mémoire
      Typage pro - claire
      Con - NsintGermax n'est pas vraiment nulle. Si les programmeurs ne sont pas prudents ou ne connaissent pas cette convention, des valeurs non valides pourraient fuir dans la couche d'affichage.
      Con - ne peut pas les stocker dans une collection sans conversions dans et dehors

    2. Utilisez NSNumber et désignez des types à l'aide de la notation hongroise (par exemple, Nunumber FHEight, NSNumber Eries)
      Pro - Citoyens de première classe
      Problème Pro - Nil résolu
      Con - augmentation de la mémoire au-dessus de la mémoire
      Con - perdre le type de compilateur Vérification
      La notation de la hongroise est contentieuse

    3. Écrivez mes propres types d'objets primitifs de première classe (Pensez Java http : //developer.android.com/reference/java/lang/integer.html )
      Pro - Citoyens de première classe
      Problème Pro - Nil résolu
      Pro - garde le type de compilateur Vérification de
      Pro - Les objets seront plus simples que Nsnumber. Le stockage interne sera spécifique au type de données.
      Con - augmentation de la mémoire au-dessus de la mémoire
      Sacrifie un peu de portabilité et de compatibilité de code

      À la recherche d'un argument convaincant en faveur de l'une de ces techniques, ou celui que je n'ai pas pensé si vous en avez un.


      mise à jour

      Je suis allé de l'avant et j'ai commencé un projet open source (Apache 2.0) dans lequel je tiendrai un certain nombre de nos classes internes comme j'ai le temps. Il inclut actuellement des emballages d'objets pour certains des types de données natives les plus courants (BOOL, CGFLOAT, NSINTEGER, NSUINTEGER). Nous avons choisi de faire cela car il met à niveau ces types de données aux citoyens de première classe avec une dactylographie stricte. Peut-être que vous êtes en désaccord avec cette approche, mais cela a bien fonctionné pour nous, alors n'hésitez pas à l'utiliser si vous le souhaitez.

      J'ajoute d'autres classes que nous avons trouvées utilisées pour, y compris un cache LRU à bord de disque, un objet "paire", un pool de déverrouillage de mémoire faible, etc.

      profiter GitHub - Zoosk / ZSFoundation


4 Réponses :


4
votes

Vous pouvez essayer cela?

NSNumber *myInt = [NSNumber numberWithInteger:100];
float myFloat = [myInt floatValue];


9 commentaires

C'est une mauvaise idée sans beaucoup plus de travail. nsnumber fait partie d'un cluster de classe et La documentation a une information sur ce que vous devez faire de plus


@Dave, oui, je viens de vérifier ça. Je n'ai aucune idée de ce que cela signifie, cependant. Y a-t-il quelque chose sur les clusters de classe?


@Stephen La définition de la documentation d'un cluster de classe


@Stephen - Je ne suis pas sûr que les définitions de la macro gagnent vraiment tout ce qui suffit à utiliser des nsnumbers dans ce cas. Il y a toujours l'ambiguïté autour de quel type de numéro une variable donnée est censé représenter. Et oui, je n'essaierais pas de sous-classer Nsnumber. Je préférerais probablement créer mes propres cours (par exemple, Zsinteger) qui étaient des frères et sœurs ou des enfants de Nsvalue, pas Nsnumber.


@Dave - Hey Dave, qu'en est-il de la sous-classement Nsnumber et de prouver rien du tout. Cela me donnerait au moins la capacité de désambigucher le contenu souhaité de l'objet, sans fumer avec le cluster de classe.


@Dougw qui ne fonctionnera pas. Chaque fois que vous créez une sous-classe dans un cluster de classe, vous devez remplacer certaines méthodes de la chose à jager dans son ensemble. Les documents nsnumber sont très spécifiques à ce sujet (en particulier en considérant que nsnumber est une sous-classe de nsvalue )


@Dave - Ouais je me demandais simplement si c'était quelque chose de intrinsèque au type de classe elle-même ou que je voulais dire que si vous allez gâcher avec tout ce que vous devez vous gâcher avec tout. Dans ce cas, je ne plaisante rien du tout. Pensez-vous que je pourrais juste écrire mon propre cluster de classe quand même.


@Dougw, vous ne pouvez vraiment pas gérer que vos primitives soient-elles faibles? Vous n'avez pas besoin de savoir ce que vous avez enregistré un numéro de la récupération ...


@Stephen - je veux dire, est faible en tapant la fin du monde? Non, je peux certainement travailler avec elle. Mais par exemple, si vous avez un nombre de nsnumber, vous devez comparer contre une NSuinteger, comment savez-vous ce qui est là? Est-ce non signé? Est-ce une chose de sécurité à faire? Vous devrez suivre les choses à la source et espérer que tout le monde sur le chemin fait des hypothèses sûres. Je préfère être explicite, pas pour moi-même, mais pour la collaboration et la clarté.



5
votes

La convention la plus courante pour représenter l'idée de nil en tant que nsinteger est d'utiliser la valeur nsnotfound . Ceci est, en fait, égal à Nsintgermax , bien qu'il a tendance à être plus évident pour le lecteur qu'il s'agit d'une valeur sentinelle représentant le manque de nombre. Il existe de nombreux cas où cela est utilisé dans tout le cacao. Un cas commun est comme le champ de localisation d'un Nsrange comme valeur de retour de -rangeofstring: et al.


6 commentaires

Hey, oui je suis d'accord, mais à la fin de la journée, il s'agit d'une convention et pas nécessairement sûre. Nous avons connu un certain nombre de bugs où la hauteur d'un utilisateur peut être affichée accidentellement comme Nsintegermax, par exemple, car le programmateur écrit la logique de la couche d'affichage n'a pas été au courant de la Convention.


On dirait que vous devez éduquer vos programmeurs, alors. On ne devrait jamais faire des hypothèses aveugles sur les valeurs utilisées.


Heh, d'accord. Ce n'est pas une question d'éducation, c'est une question de diffusion des connaissances. Certains nsintegers peuvent utiliser cette convention et certains ne peuvent pas. Et qu'en est-il de CGFLoats? Si personne ne pose rien de mieux bientôt, je vais aller de l'avant et marquer cela la réponse, car il ne peut y avoir de meilleure solution.


Si vos programmeurs ne savent pas qu'il existe une valeur sentinelle, la valeur que vous utilisez ne fera pas beaucoup de différence. Le cacao utilise presque exclusivement Nsnotfound comme valeur Sentinel Nsinteger / Nsuinteger. En ce qui concerne CGFLOt, les valeurs sentinelles ne sont pas si communes là-bas, mais vous pourriez utiliser NAN, bien que si vous faites, vous devriez faire très attention à la façon dont vous le gérez. Par exemple, dans iOS 4.2, la définition d'une composante Calayer Centre / limites sur Nan déclenche une exception.


Exactement mon point. Avec Nil, personne n'a besoin de "être au courant" de quoi que ce soit - il est dicté par l'architecture, pas la convention. Si je vous ai donné une grosse bibliothèque de mon code, comment sauriez-vous quelle convention j'ai utilisée? Allez-vous rechercher ma documentation pour chaque propriété et confiance que j'ai documentées correctement partout? Je vais aller de l'avant et accepter cela puisqu'il n'y a pas une solution native meilleure, mais que je suis un désastre dans une langue fondée sur des valeurs de messages de messagerie en échec et de NIL.


Cette réponse a simplifié les subtilités de la question en se concentrant uniquement sur les conventions de Nsinteger. Cependant, les commentaires ultérieurs de Kevin indiquent d'autres méthodes de traitement des autres types. Dans n'importe quel langage orienté objet (Ruby vient à l'esprit en particulier) Les classes de nombres abstraites sont un compromis épineux.



3
votes

Autre alternative, il existe également la structure nsdecimale pour représentant des types numériques. Nsdecimal (et sa version de classe Objective-C Nsdecimalnumber) vous permet de représenter de véritables décimales et d'éviter les erreurs de points flottantes, il a donc tendance à être Recommandé pour traiter des choses telles que la monnaie .

La struct Nsdecimal peut représenter des nombres ainsi que l'état d'un numéro (un substitut potentiel NIL). Vous pouvez interroger si un nsdecimal n'est pas un numéro en utilisant nsdecimalisnotangraphogh () et générer la valeur de numéros non à l'aide d'un nsdecimalnumber.

nsdecimaux sont plus rapide de travailler avec que Nsdecimalnumbers , et les structures n'apportent pas le même type de problèmes de gestion de la mémoire que font des objets.

Cependant, il n'y a pas de moyen facile d'obtenir des valeurs dans la forme Nsdecimale sans utiliser de NsdecimalNumber temporaire. De plus, bon nombre des fonctions mathématiques (telles que les opérations de trigonométrie) disponibles pour de simples numéros de points flottants ne sont pas encore disponibles pour Nsdecimal. J'aimerais écrire mes propres fonctions qui ajoutent certaines de ces capacités, mais ils auraient besoin d'accéder aux champs internes de la structure. Apple étiquette cela comme privé (avec l'utilisation d'un soulignement), mais ils sont présents dans les en-têtes et je suppose qu'ils ne changent probablement pas à l'avenir.

Mise à jour: Dave Delong a écrit une série de fonctions pour effectuer une trigonométrie nsdecimale, des racines et d'autres opérations. J'ai modifié celles-ci pour fournir jusqu'à 34 chiffres de précision décimale, et le code de ces fonctions peut être téléchargé ici . En tant qu'enviction, cette précision vient à un prix en termes de performance, mais ces fonctions doivent être correctes pour des calculs assez rares.


4 commentaires

Si vous écrivez vos propres fonctions de Trig pour nsdecimal , j'aimerais savoir à ce sujet!


Intéressant, je n'ai pas utilisé Nsdecimal avant. Je ne sais pas qu'il résout mon problème, mais merci de le pointer.


Nsdecimalnumber est une sous-classe de Nsnumber.


@JeremyP - Correct, mais je voulais signaler la struct Nsdecimal, qui est un autre type numérique non-objet. Certes, vous devez toucher Nsdecimalnumber à un moment donné lorsqu'il y a affaire, mais Nsdecimal peut avoir des avantages dans certaines situations.



0
votes

Je ne comprends pas pourquoi, si vous stockez des choses en tant que nsnumber, vous devez vous soucier de ce type. Nsnumber choisira une représentation interne appropriée et effectuera ensuite une conversion nécessaire en fonction du format que vous demandez sa valeur dans.

Si vous ne pouvez pas dire quel type est approprié du nom de la variable, vous devez choisir de meilleurs noms. par exemple. P>

numberOfChildren = [NSNumber numberWithFloat: 2.5];


2 commentaires

Merci pour la réponse Jeremy. Je pense que si vous recherchez la controverse autour de la notation hongroise, vous trouverez de nombreux arguments contre l'utilisation d'une convention de dénomination qui explique le type de la variable. Bien sûr, les numéros de scolarité sont évidents, mais que diriez-vous de "Heightininches"? Est-ce un entier ou permet-il de faire des pouces fractionnaires? Il existe une variété de cas où la plage de valeurs autorisée n'est pas claire, même avec une bonne nommée. La raison pour laquelle je me soucie, c'est parce que je veux être explicite sur des choses comme signées vs non signées et int Vs flottent à travers la pile.


J'ajouterai qu'il y a un certain nombre de langues faiblement ou même non typées, et c'est une approche parfaitement valide. C et Obj C sont toutefois conçus pour être fortement typés, et il y a beaucoup de raisons de ne pas rompre cette construction. Donc, je ne dis pas que vous ne pouviez pas le faire votre chemin, je préfère juste rester dans le domaine de la frappe forte. Cela conduit à une autre autre débat, donc pour ma question particulière, j'aimerais coller à fort.