Mengapa variabel NSInteger harus dilemparkan lama ketika digunakan sebagai argumen format?

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

Kode di atas menghasilkan kesalahan:

Nilai tipe 'NSInteger' tidak boleh digunakan sebagai format argumen; tambahkan cast eksplisit ke 'long' sebagai gantinya

NSLogPesan yang dikoreksi sebenarnya NSLog(@"%lg", (long) myInt);. Mengapa saya harus mengonversi nilai integer myIntmenjadi longjika saya ingin nilai ditampilkan?

Daniel Lee
sumber
1
@DanielLee, jika Anda menggunakan NSLog(@"%ld", (long) myInt);, para longpemain harus membuatnya sesuai dengan lkualifikasi %ld, tetapi semua itu tidak perlu karena NSLog(@"%d", myInt);cukup (mengingat bahwa kita dapat melihat bahwa myInttidak long. Intinya, Anda myIntmenggunakan jika menggunakan kualifikasi panjang dalam format string, tetapi tidak perlu menggunakan kualifikasi format string panjang atau longdilemparkan ke sini
Rob
1
Rupanya, itu tidak benar bahwa NSLog (@ "% i", myInt); sudah cukup karena Anda akan mendapatkan pesan kesalahan seperti yang saya tunjukkan di atas.
Daniel Lee
2
@DanielLee Lihat komentar Martin R. Anda diposting pertanyaan Anda dengan tag iOS (mana NSIntegeryang tidak lama), tapi kedengarannya seperti Anda kompilasi target OS X (di mana NSInteger adalah long ).
Rob
Ahh, begitu. Saya tidak tahu iOS dan OSX akan membuat NSInteger berbeda dalam hal bit dan tipe.
Daniel Lee

Jawaban:

193

Anda mendapatkan peringatan ini jika Anda mengkompilasi pada OS X (64-bit), karena pada platform NSIntegeritu didefinisikan sebagai longdan adalah integer 64-bit. The %iFormat, di sisi lain, adalah untuk int, yang 32-bit. Jadi format dan parameter aktual tidak sesuai ukurannya.

Karena NSInteger32-bit atau 64-bit, tergantung pada platform, kompiler merekomendasikan untuk menambahkan gips longsecara umum.

Pembaruan: Karena iOS 7 sekarang mendukung 64-bit juga, Anda bisa mendapatkan peringatan yang sama saat mengkompilasi untuk iOS.

Martin R
sumber
1
Saya mendapatkan kesalahan ini di iOS 7. Karena hanya iPhone 5S terbaru yang 64 bit jika saya atur selama itu akan menyebabkan masalah pada perangkat 32 bit yang lebih lama?
Pritesh Desai
25
@ BartSimpson: Dengan case eksplisit ke "long", seperti pada NSLog(@"%ld", (long) myInt), ia bekerja pada 32-bit dan 64-bit dengan benar.
Martin R
@ MartinR jika kita casting, mengapa tidak menggunakan yang lama?
William Entriken
3
@FullDecent: Tentu saja Anda dapat bekerja dengan lama di sini: long myInt = [myNumber longValue];. Tetapi banyak (Core) metode Foundation menggunakan NS (U) Integer sebagai parameter atau nilai kembali, sehingga masalah umum tetap ada. Juga masuk akal di aplikasi Anda untuk menggunakan NS (U) Integer untuk mendapatkan jangkauan yang lebih besar pada perangkat 64-bit.
Martin R
39

Anda tidak perlu melakukan apa pun jika specifier format Anda cocok dengan tipe data Anda. Lihat jawaban Martin R untuk perincian tentang bagaimana NSIntegerdidefinisikan dalam hal jenis asli.

Jadi untuk kode yang dimaksudkan untuk dibangun untuk lingkungan 64-bit, Anda dapat menulis pernyataan log Anda seperti ini:

NSLog(@"%ld",  myInt); 

sedangkan untuk lingkungan 32-bit Anda dapat menulis:

NSLog(@"%d",  myInt); 

dan itu semua akan bekerja tanpa gips.

Salah satu alasan untuk menggunakan gips adalah karena kode yang baik cenderung untuk porting di seluruh platform, dan jika Anda melemparkan variabel Anda secara eksplisit itu akan dikompilasi dengan bersih pada 32 dan 64 bit:

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

Dan perhatikan ini benar bukan hanya untuk pernyataan NSLog, yang hanya merupakan alat bantu debugging, tetapi juga untuk [NSString stringWithFormat:]dan berbagai pesan turunan, yang merupakan elemen sah dari kode produksi.

Monolo
sumber
1
Jadi sekarang ini diperlukan hack, apakah masih praktik terbaik untuk menggunakan NSInteger di tempat pertama?
William Entriken
@FullDecent Hanya masalah dalam kode yang ditafsirkan runtime, seperti format string. Semua kode yang dikompilasi mengambil keuntungan dari typedef NSInteger.
Monolo
Ini adalah praktik terbaik untuk menggunakan NSInteger, karena ada alasan bagus mengapa ia didefinisikan seperti itu didefinisikan.
gnasher729
22

Alih-alih meneruskan NSInteger ke NSLog, hanya meneruskan NSNumber. Ini akan berkeliling semua gips dan memilih specifier format string yang tepat.

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

Ini juga berfungsi untuk NSUIntegers tanpa harus khawatir tentang itu. Lihat jawaban untuk NSInteger dan NSUInteger dalam lingkungan campuran 64bit / 32bit

orkoden
sumber
2
Saya kira jawaban yang dipilih secara teknis adalah jawaban terbaik untuk pertanyaan itu, tetapi jika Anda ingin tahu bagaimana cara menghindari setiap kejadian dan mencegah peringatan maka saya menemukan ini adalah solusi terbaik.
Daniel Wood
0

Itu terus peringatan saat menggunakan NSLog(@"%ld", (long)myInt);, tetapi berhenti peringatan setelah mengubah deklarasi ke long myInt = 1804809223;di iOS 10.

Yao Li
sumber
-2

OS X menggunakan beberapa tipe data — NSInteger, NSUInteger, CGFloat, dan CFIndex — untuk menyediakan cara yang konsisten dalam merepresentasikan nilai dalam lingkungan 32-dan 64-bit. Dalam lingkungan 32-bit, NSInteger dan NSUInteger didefinisikan sebagai int dan unsigned int, masing-masing. Dalam lingkungan 64-bit, NSInteger dan NSUInteger masing-masing didefinisikan sebagai panjang dan tidak bertanda. Untuk menghindari perlunya menggunakan specifier tipe gaya printf yang berbeda tergantung pada platform, Anda dapat menggunakan specifier yang ditunjukkan dalam tautan ini untuk lingkungan 32 bit dan 64 bit.

Saheb Singh
sumber