Warum muss eine NSInteger-Variable als Formatargument zu lang umgewandelt werden?

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

Der obige Code erzeugt einen Fehler:

Werte vom Typ 'NSInteger' sollten nicht als Formatargumente verwendet werden. Fügen Sie stattdessen 'explizit' eine explizite Besetzung hinzu

Die korrigierte NSLogNachricht ist tatsächlich NSLog(@"%lg", (long) myInt);. Warum muss ich den ganzzahligen Wert von in konvertieren myInt, longwenn der Wert angezeigt werden soll?

Daniel Lee
quelle
1
@DanielLee, wenn Sie verwenden NSLog(@"%ld", (long) myInt);, das longgegossen wird, um es mit dem oben zu machen Match lQualifier %ld, aber all das ist nicht notwendig , da NSLog(@"%d", myInt);ausreichend ist (vorausgesetzt , dass wir sehen können , dass myIntnicht long. Unterm Strich, werfen Sie myIntbei Verwendung von langen Qualifikationsspiel in Format Zeichenfolge, aber keine Notwendigkeit, entweder langes Zeichenfolgenformat-Qualifikationsmerkmal zu verwenden oder longhier
Rob
1
Anscheinend ist es nicht wahr, dass NSLog (@ "% i", myInt); ist ausreichend, da Sie die oben gezeigte Fehlermeldung erhalten.
Daniel Lee
2
@ DanielLee Siehe Martin Rs Kommentar. Sie haben Ihre Frage mit dem iOS-Tag gepostet (wo NSIntegerist nicht lang), aber es hört sich so an, als würden Sie mit dem OS X-Ziel kompilieren (wo NSInteger ist long ).
Rob
Ahh, ich verstehe. Ich wusste nicht, dass iOS und OSX die NSInteger in Bit und Typ unterscheiden würden.
Daniel Lee

Antworten:

193

Sie erhalten diese Warnung, wenn Sie unter OS X (64-Bit) kompilieren, da auf dieser Plattform eine 64-Bit-Ganzzahl NSIntegerdefiniert longist. Das %iFormat ist dagegen für int32-Bit. Das Format und der tatsächliche Parameter stimmen also nicht in der Größe überein.

Da NSIntegeres sich je nach Plattform um 32-Bit oder 64-Bit handelt, empfiehlt der Compiler, longallgemein eine Umwandlung hinzuzufügen .

Update: Da iOS 7 jetzt auch 64-Bit unterstützt, können Sie beim Kompilieren für iOS dieselbe Warnung erhalten.

Martin R.
quelle
1
Ich erhalte diesen Fehler unter iOS 7. Da nur das neueste iPhone 5S 64-Bit ist, wenn ich es so lange einstelle, wird es auf älteren 32-Bit-Geräten Probleme verursachen?
Pritesh Desai
25
@BartSimpson: Mit einem expliziten Fall zu "lang", wie in NSLog(@"%ld", (long) myInt), funktioniert es auf 32-Bit und 64-Bit korrekt.
Martin R
@MartinR Wenn wir gießen, warum nicht einfach lange verwenden?
William Entriken
3
@FullDecent: Natürlich können Sie hier lange arbeiten : long myInt = [myNumber longValue];. Viele (Core) Foundation-Methoden verwenden jedoch NS (U) Integer als Parameter oder Rückgabewert, sodass das allgemeine Problem weiterhin besteht. In Ihrer App kann es auch sinnvoll sein, NS (U) Integer zu verwenden, um einen größeren verfügbaren Bereich auf 64-Bit-Geräten zu erhalten.
Martin R
39

Sie müssen nichts umwandeln, wenn Ihre Formatspezifizierer Ihren Datentypen entsprechen. In der Antwort von Martin R finden Sie Details dazu, wie NSIntegerin Bezug auf native Typen definiert wird.

Für Code, der für 64-Bit-Umgebungen erstellt werden soll, können Sie Ihre Protokollanweisungen wie folgt schreiben:

NSLog(@"%ld",  myInt); 

In 32-Bit-Umgebungen können Sie Folgendes schreiben:

NSLog(@"%d",  myInt); 

und es wird alles ohne Abgüsse funktionieren.

Ein Grund für die Verwendung von Casts ist, dass guter Code in der Regel plattformübergreifend portiert wird. Wenn Sie Ihre Variablen explizit umwandeln, wird er sauber auf 32- und 64-Bit kompiliert:

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

Beachten Sie, dass dies nicht nur für NSLog-Anweisungen gilt, die schließlich nur Debugging-Hilfsmittel sind, sondern auch für [NSString stringWithFormat:]und die verschiedenen abgeleiteten Nachrichten, die legitime Elemente des Produktionscodes sind.

Monolo
quelle
1
Ist es nun, da dieser Hack erforderlich ist, immer noch die beste Vorgehensweise, NSInteger überhaupt zu verwenden?
William Entriken
@FullDecent Es ist nur ein Problem im Code, das zur Laufzeit interpretiert wird, z. B. Formatzeichenfolgen. Jeder kompilierte Code nutzt den NSInteger-Typedef.
Monolo
Es wird empfohlen, NSInteger zu verwenden, da es gute Gründe gibt, warum es so definiert wird, wie es definiert ist.
Gnasher729
22

Anstatt eine NSInteger an NSLog zu übergeben, übergeben Sie einfach eine NSNumber. Dadurch werden alle Casts umgangen und der richtige String-Format-Bezeichner ausgewählt.

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

Es funktioniert auch für NSUIntegers, ohne sich darum kümmern zu müssen. Siehe Antwort auf NSInteger und NSUInteger in einer gemischten 64-Bit / 32-Bit-Umgebung

Orkoden
quelle
2
Ich nehme an, die ausgewählte Antwort ist technisch die beste Antwort auf die Frage, aber wenn Sie wissen möchten, wie Sie vermeiden können, jedes Vorkommen zu werfen und Warnungen zu vermeiden, dann finde ich, dass dies die beste Lösung ist.
Daniel Wood
0

Während der Verwendung bleibt die Warnung erhalten, die Warnung NSLog(@"%ld", (long)myInt);wird jedoch nach der Änderungsdeklaration long myInt = 1804809223;in iOS 10 beendet.

Yao Li
quelle
-2

OS X verwendet verschiedene Datentypen - NSInteger, NSUInteger, CGFloat und CFIndex -, um eine konsistente Darstellung von Werten in 32- und 64-Bit-Umgebungen bereitzustellen. In einer 32-Bit-Umgebung werden NSInteger und NSUInteger als int bzw. unsigned int definiert. In 64-Bit-Umgebungen werden NSInteger und NSUInteger als long bzw. unsigned long definiert. Um zu vermeiden, dass je nach Plattform unterschiedliche Druckspezifizierer vom Typ printf verwendet werden müssen, können Sie die in diesem Link gezeigten Spezifizierer sowohl für 32-Bit- als auch für 64-Bit-Umgebungen verwenden.

Saheb Singh
quelle