Dlaczego zmienna NSInteger musi być rzutowana zbyt długo, gdy jest używana jako argument formatu?

143
NSInteger myInt = 1804809223;
NSLog(@"%i", myInt); <==== 

Powyższy kod powoduje błąd:

Wartości typu „NSInteger” nie powinny być używane jako argumenty formatu; zamiast tego dodaj wyraźne rzutowanie do „długiego”

Poprawiony NSLogkomunikat to faktycznie NSLog(@"%lg", (long) myInt);. Dlaczego muszę konwertować wartość całkowitą myIntna, longjeśli chcę, aby wartość była wyświetlana?

Daniel Lee
źródło
1
@DanielLee, jeśli używasz NSLog(@"%ld", (long) myInt);, longobsada ma dopasować ją do lkwalifikatora %ld, ale wszystko to jest niepotrzebne, ponieważ NSLog(@"%d", myInt);jest wystarczające (biorąc pod uwagę, że widzimy, że myIntnie jest long. Podsumowując, rzucasz, myIntjeśli używasz długiego kwalifikatora w formacie string, ale nie ma potrzeby używania w tym miejscu kwalifikatora formatu długich łańcuchów ani longrzutowania
Rob
1
Najwyraźniej nie jest prawdą, że NSLog (@ "% i", myInt); jest wystarczające, ponieważ otrzymasz komunikat o błędzie, jak pokazałem powyżej.
Daniel Lee,
2
@DanielLee Zobacz komentarz Martina R. Opublikowałeś swoje pytanie z tagiem iOS (gdzie nieNSInteger jest długi), ale brzmi to tak, jakbyś kompilował z celem OS X (gdzie jest ). NSInteger long
Rob
Ahh rozumiem. Nie wiedziałem, że iOS i OSX sprawią, że NSInteger będzie inny pod względem bitu i typu.
Daniel Lee,

Odpowiedzi:

193

To ostrzeżenie jest wyświetlane, jeśli kompilujesz w systemie OS X (64-bitowym), ponieważ na tej platformie NSIntegerjest zdefiniowana jako longi jest 64-bitową liczbą całkowitą. Z %idrugiej strony format jest przeznaczony dlaint 32-bitowych. Zatem format i rzeczywisty parametr nie pasują do rozmiaru.

Ponieważ NSIntegerjest 32-bitowy lub 64-bitowy, w zależności od platformy, kompilator zaleca dodanie rzutowania dolong ogólnie .

Aktualizacja: ponieważ iOS 7 obsługuje teraz również wersję 64-bitową, możesz otrzymać to samo ostrzeżenie podczas kompilacji dla iOS.

Martin R.
źródło
1
Ten błąd pojawia się na iOS 7. Skoro tylko najnowszy iPhone 5S jest 64-bitowy, jeśli go ustawię, czy będzie powodował problemy na starszych urządzeniach 32-bitowych?
Pritesh Desai
25
@BartSimpson: Z jawną wielkością liter jako „long”, jak w przypadku NSLog(@"%ld", (long) myInt), działa poprawnie na 32-bitowym i 64-bitowym.
Martin R
@MartinR, jeśli zajmujemy się castingiem, to czemu po prostu nie używać long na pierwszym miejscu?
William Entriken,
3
@FullDecent: Oczywiście można pracować długo tutaj: long myInt = [myNumber longValue];. Jednak wiele metod (Core) Foundation używa NS (U) Integer jako parametru lub wartości zwracanej, więc ogólny problem pozostaje. W Twojej aplikacji może mieć również sens użycie NS (U) Integer, aby uzyskać większy dostępny zakres na urządzeniach 64-bitowych.
Martin R
39

Nie musisz rzucać do niczego, jeśli specyfikatory formatu pasują do twoich typów danych. Zobacz odpowiedź Martina R., aby dowiedzieć się, jak to zrobićNSInteger definicji w kategoriach typów natywnych.

Tak więc w przypadku kodu przeznaczonego do tworzenia dla środowisk 64-bitowych możesz pisać instrukcje dziennika w następujący sposób:

NSLog(@"%ld",  myInt); 

podczas gdy dla środowisk 32-bitowych możesz napisać:

NSLog(@"%d",  myInt); 

i będzie działać bez odlewów.

Jednym z powodów, dla których warto mimo wszystko używać rzutowań, jest to, że dobry kod jest zwykle przenoszony między platformami, a jeśli rzutujesz zmienne jawnie, skompiluje się on czysto zarówno na 32, jak i 64 bitach:

NSLog(@"%ld",  (long)myInt);

Zauważ, że dotyczy to nie tylko instrukcji NSLog, które w końcu są tylko pomocami w debugowaniu, ale także [NSString stringWithFormat:]różnych komunikatów pochodnych, które są uprawnionymi elementami kodu produkcyjnego.

Monolo
źródło
1
Więc teraz, gdy ten hack jest wymagany, czy nadal najlepszą praktyką jest używanie NSInteger w pierwszej kolejności?
William Entriken,
@FullDecent Jest to tylko problem w kodzie, który jest interpretowany w czasie wykonywania, taki jak ciągi formatujące. Każdy skompilowany kod wykorzystuje typedef NSInteger.
Monolo
To jest najlepsza praktyka w obsłudze NSInteger, ponieważ istnieją dobre powody, dla których został on sformułowany w sposób, w jaki jest zdefiniowany.
gnasher729
22

Zamiast przekazywać NSInteger do NSLog, wystarczy przekazać NSNumber. Spowoduje to obejście wszystkich rzutów i wybranie odpowiedniego specyfikatora formatu ciągu.

NSNumber foo = @9000;
NSLog(@"foo: %@", foo);
NSInteger bar = 9001;
NSLog(@"bar: %@", @(bar));

Działa również w przypadku NSUIntegers, nie martwiąc się o to. Zobacz odpowiedź na NSInteger i NSUInteger w mieszanym środowisku 64-bitowym / 32-bitowym

orkoden
źródło
2
Przypuszczam, że wybrana odpowiedź jest technicznie najlepszą odpowiedzią na pytanie, ale jeśli chcesz wiedzieć, jak uniknąć rzutowania każdego wystąpienia i zapobiec ostrzeżeniom, to uważam, że jest to najlepsze rozwiązanie.
Daniel Wood,
0

Zachowuje ostrzeżenie podczas używania NSLog(@"%ld", (long)myInt);, ale przestaje ostrzegać po deklaracji zmiany na long myInt = 1804809223;w iOS 10.

Yao Li
źródło
-2

OS X używa kilku typów danych - NSInteger, NSUInteger, CGFloat i CFIndex - aby zapewnić spójne sposoby przedstawiania wartości w środowiskach 32- i 64-bitowych. W środowisku 32-bitowym NSInteger i NSUInteger są zdefiniowane odpowiednio jako int i unsigned int. W środowiskach 64-bitowych NSInteger i NSUInteger są definiowane odpowiednio jako long i unsigned long. Aby uniknąć konieczności używania różnych specyfikatorów typu printf w zależności od platformy, można użyć specyfikatorów pokazanych w tym łączu zarówno dla środowiska 32-bitowego, jak i 64-bitowego.

Saheb Singh
źródło