Attention aux NSUInteger
colas_
Membre
J'avais déjà remarqué que les NSUInterger n'aiment pas les soustractions.
Mais, ce qui suit, je n'aurais jamais pensé !!!
NSUInteger index = 0 ;
if (index > -1)
{
NSLog(@OK) ;
}
else
{
NSLog(@This is so tricky) ;
}
J'aime bien les NSUInteger car ils s'intègrent bien au framework cocoa. Par la suite, je pense que j'adopterais le pattern suivant :
- (void)methodWith:(NSUInteger)_aNumber
{
NSInteger aNumber = _aNumber ;
// use only aNumber, not _aNumber
}
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Alors, oui et non.
Un NSUInteger, is unsigned. Du coup, il "boucle" sur du positif, plutôt que de passer à du négatif, où un de ses bits lui dit qu'il est négatif ou positif.
Par exemple, vu que j'ai remarqué ce cas là quand j'ai récupéré un commit tout à l'heure au boulot de mon collègue qui développe avec un iPad 2 (32Bits) et qui ne voit donc pas les warnings liés au 64 bits, que XCode t'avertit si tu fais if (monNSUInteger > 0) que ça sera vrai dans tous les cas.
Le problème avec ton "pattern", c'est que si _aNumber est très grand (bit de poids le plus fort à 1), la conversion en NSInteger va générer un nombre négatif...
Non, franchement, NSUInteger (et les scalaires non signés en général) ne posent aucun problème (même pas pour les soustractions) si on fait attention à ce que l'on fait...
C'est un héritage du C, les types débordent. Il faut en tenir compte.
Donc le seul pattern valable c'est
En Swift (ou en C++), grâce à la surcharge des opérateurs, tu peux développer un type unsigned qui ne déborde pas.
Du coup si tu laisses ce code, soit il va convertir -1 en unsigned, et interpréter un nombre négatif comme un un unsigned va l'interpréter, à cause de l'underflow, comme un nombre super grand... soit il va convertir ton NSUInteger en signé (NSInteger), et du coup si ton nombre est super grand il va l'interpréter comme un nombre négatif. Dans tous les cas c'est pas cohérent. Faut pas comparer des patates et des carottes, même si ce sont tous les deux des légumes.
Et la réponse qu'il me donne est "It is so tricky". En pratique, les entiers que j'utilise ne sont jamais très grands. Je compte des vues, etc.
Je travaille avec des index, de type NSUInteger.
J'ai une @property NSInteger maxIndex. Elle vaut -1 s'il n'y a aucun index.
Dans une des méthodes qui prend en paramètre un index (NSUInteger), je teste
Ben c'est ce que se passe quand on compare des carottes a des navets...
Il me semble qu'en C les operateurs binaires de comparaison travaillent justement en binaire. Les litéraux sont donc utilisés tels quels.
Pour rappel sur 32bit -1 fait :
soit 9223372036854775807 quand on le considère comme un entier non signé...
(pas de bit de signe mais le fameux complément à deux)
C'est classe ce truc !
Y'a pas mal d'API de Cocoa, écrites du temps d'Objective-C, qui utilisent cette valeur de retour.
- Genre toutes les méthodes "rangeOfXXX" de NSString (rangeOfString:, rangeOfCharactersFromSet:, etc) retournent un NSRange qui indique l'emplacement en question si l'élément a été trouvé, et un NSRange dont la location vaut NSNotFound (et de longueur 0) s'il n'a pas été trouvé.
- De même pour toutes les méthodes de NSArray du genre "indexOfObject:", qui retournent NSNotFound si l'élément n'a pas été trouvé.
Donc ça date pas d'hier cet astuce, et c'est aussi pour ça que je te conseille d'utiliser cette même constante NSNotFound si tu veux en faire un usage similaire pour ton code avec maxIndex.
Après, avec Swift et la possibilité d'avoir des "Int?" donc des entiers optionals qui peuvent être nil, c'est sûr qu'on a bien mieux maintenant, mais bon si t'es obligé de continuer à faire de l'Objective-C en attendant faut se contenter de NSNotFound
Non, ça c'était pour recalculer le maxIndex !
Je connaissais NSNotFound. Mais, ici en tant que valeurMax invalide, je pense que -1 est plus logique :P (car NSNotFound est très grand !!)
Je trouve qu'évaluer (NSUInteger)0 > -1 à NO, c'est quand même sioux ! Je comprends que c'est logique mais on aurait pu aussi imaginer qu'il fasse un cast automatique dans ce cas. Je le signalais car ça peut être à l'origine de bugs sioux.
Et oui, avec swift, c'est maintenant beaucoup mieux !!
Est-ce qu'on ne pourrait pas (dans le cas par exemple où l'on a ≥2 valeurs spéciales et non entières) étendre Int (créer un sous-type de Int) et ajouter des symboles pour ces valeurs à exclure ?
Je pense qu'il fait bien un cast automatique, mais de -1 pas de ta variable.
Mais il FAIT un cast automatique justement ! Il cast implicitement ce "-1" en NSUInteger, ce qui l'évalue alors à ((NSUInteger)(-1)) = NSIntegerMax, et c'est justement pour ça que c'est évalué à NO.
Tout à fait, et c'est pour ça qu'il y a un warning. Je t'invite fortement à activer ces warnings par défaut sur tous tes projets (c'est bien dommage qu'ils ne le soient pas par défaut " du moins pas avec les projets créés avec les anciennes versions de Xcode je sais pas si ça a changé depuis)
1) Le warning "Sign Comparison" (GCC_WARN_SIGN_COMPARE, -Wsign-compare) est celui qui va te générer un warning si tu compares un NSUInteger avec un NSInteger, et t'aurais justement mis la puce à l'oreille si tu l'avais activé sur ton code. Celui là faut pas hésiter, je comprend même pas pourquoi il est pas à YES dans les "iOS Defaults".
2) Tu as aussi le warning "Implicit Signess Conversions" (CLANG_WARN_IMPLICIT_SIGN_CONVERSION) qui va quant à lui te signaler si tu as des conversions implicites d'un signed vers un unsigned ou vice-versa, te forçant à caster explicitement si c'est vraiment ce que tu veux.
Lui il est un peu violent, car par exemple quand tu implémenter UITableViewDataSource et sa méthode numberOfRowsInSeciton, qui est sensé retourner un NSInteger (eh oui, il est pas Unsigned celui-là , j'ai jamais trop compris pourquoi, ça vient sans doute de NSIndexPath qui contient représente une suite index non signés et ne sert pas qu'à UITableView), et du coup si tu "return myModelArray.count" il va te mettre un warning car NSArray.count est un NSUInteger, donc tu devras faire un cast explicite à chaque fois... mais bon au moins ça te fait réaliser où sont faites les conversions et tu le feras en connaissance de cause.
Non en Swift si tu as plus d'une valeur spéciale (donc pas juste "une valeur ou bien nil", tu vas créer un enum avec associated value. D'ailleurs, un Optional n'est rien d'autre qu'un enum avec 2 cas possibles : un "case None" qui représente l'absence de valeur, et un "case Some(value)" qui représente la présence d'une valeur, avec ladite valeur alors associée. Du coup si tu as besoin de + d'un cas d'exception, tu vas faire un enum aux petits oignons avec des "case" qui seront nommés spécifiquement pour ton cas particulier et pourront représenter tous tes états possibles. Genre "case NilValue", "case UnknownValue", "case InvalidValue", "case Error(NSError)", et bien sûr le "case Value(Int)", pour représenter une absence de valeur, une valeur inconnue, une valeur invalide, un cas d'erreur lors de la récupération de la valeur, et le cas droit où t'as une véritable valeur de type Int.
Bon à savoir !
Merci !