Peringatan: "format bukan string literal dan tanpa argumen format"

110

Sejak memutakhirkan ke Xcode 3.2.1 dan Snow Leopard terbaru, saya telah mendapatkan peringatan

"format bukan string literal dan tanpa argumen format"

dari kode berikut:

NSError *error = nil;

if (![self.managedObjectContext save:&error]) 
{
    NSLog([NSString stringWithFormat:@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]]);      

}

Jika errorMsgFormatadalah NSStringdengan format specifiers (misalnya: "print me like this: %@"), apa yang salah dengan di atas NSLogpanggilan? Dan apa cara yang disarankan untuk memperbaikinya sehingga peringatan tidak dibuat?

Alexi Groove
sumber

Jawaban:

113

Apakah Anda menyusun tanda kurung dengan benar? Saya tidak berpikir NSLog()suka mengambil hanya satu argumen, itulah yang Anda sampaikan. Juga, itu sudah melakukan pemformatan untuk Anda. Mengapa tidak melakukan ini saja?

NSLog(@"%@ %@, %@", 
   errorMsgFormat, 
   error, 
   [error userInfo]);              

Atau, karena Anda mengatakan errorMsgFormatadalah string format dengan tempat penampung tunggal, apakah Anda mencoba melakukan ini?

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
Sixten Otto
sumber
14
"Saya tidak berpikir NSLog () suka mengambil hanya satu argumen" NSLog()dapat mengambil satu argumen, ketika string format tidak berisi penentu format.
pengguna102008
Memberikan peringatan lain. Argumen data tidak digunakan oleh format string.
hasan
157

Xcode mengeluh karena ini adalah masalah keamanan.

Berikut kode yang mirip dengan milik Anda:

NSString *nameFormat = @"%@ %@";
NSString *firstName = @"Jon";
NSString *lastName = @"Hess %@";
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);

Pernyataan NSLog terakhir itu akan menjalankan yang setara dengan ini:

NSLog(@"Jon Hess %@");

Itu akan menyebabkan NSLog mencari satu argumen string lagi, tetapi tidak ada satu pun. Karena cara kerja bahasa C, ia akan mengambil beberapa penunjuk sampah acak dari tumpukan dan mencoba memperlakukannya seperti NSString. Ini kemungkinan besar akan merusak program Anda. Sekarang string Anda mungkin tidak memiliki% @ di dalamnya, tetapi suatu hari nanti mungkin. Anda harus selalu menggunakan string format dengan data yang Anda kontrol secara eksplisit sebagai argumen pertama ke fungsi yang mengambil string format (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Seperti yang ditunjukkan Otto, Anda mungkin harus melakukan sesuatu seperti:

NSLog(errorMsgFormat, error, [error userInfo]);
Jon Hess
sumber
17
Dan sekali lagi di SO, jawaban yang rinci dan bagus jatuh ke pinggir jalan. TERIMA KASIH telah menjelaskan ini sepenuhnya. Saya tidak akan pernah menemukan ini.
Dan Rosenstark
38

Jawaban akhir: Seperti yang dikatakan Jon Hess, ini adalah masalah keamanan karena Anda mengirimkan string APA PUN ke fungsi yang mengharapkan string format. Artinya, ini akan mengevaluasi semua penentu format DALAM string apa pun. Jika tidak ada, luar biasa, tetapi jika ada, hal buruk bisa terjadi.

Hal yang tepat untuk dilakukan, kemudian, adalah MENGGUNAKAN string format secara langsung, misalnya

NSLog(@"%@", myNSString);

Dengan begitu, meskipun ada penentu format di myNSString, mereka tidak dievaluasi oleh NSLog.

Alex Whittemore
sumber
13

Saya tidak merekomendasikan penggunaan ini secara khusus, karena peringatan IS adalah peringatan nyata .. dalam penggunaan bahasa yang dinamis, dimungkinkan untuk melakukan sesuatu secara runtime ke string (yaitu memasukkan informasi baru atau bahkan merusak program) .. Namun mungkin saja untuk menekan secara paksa jika Anda TAHU seharusnya seperti ini dan Anda benar-benar tidak ingin diperingatkan ..

#pragma GCC diagnostic ignored "-Wformat-security"

Akan memberi tahu GCC untuk sementara mengabaikan peringatan kompilasi .. Sekali lagi ini tidak menyelesaikan apa pun tetapi mungkin ada kalanya Anda tidak dapat menemukan cara yang baik untuk benar-benar memperbaiki masalah.

EDIT: Pada dentang, pragma telah berubah. Lihat ini: https://stackoverflow.com/a/17322337/3937

Qrikko
sumber
10

Cara tercepat untuk memperbaikinya adalah dengan menambahkan @"%@",argumen pertama ke NSLogpanggilan Anda , yaitu,

NSLog(@"%@", [NSString stringWithFormat: ....]);

Padahal, Anda mungkin harus mempertimbangkan jawaban Sixteen Otto.

Anthony Cramp
sumber
10

Saya baru saja melewati nol untuk meniadakan peringatan, mungkin itu akan berhasil untuk Anda?

NSLog (myString, nil);

Martytoof
sumber
5
Adakah yang bisa menjelaskan MENGAPA memberikan nol sebagai parameter kedua menyelesaikan peringatan?
cprcrack
1
Meneruskan nil adalah eksplisit sedangkan kekurangan parameter kedua tidak. Anda dapat berasumsi bahwa perapian Anda tidak menyala ketika Anda meninggalkan rumah atau Anda dapat memastikan bahwa tidak menyala. Meskipun biasanya tidak ada yang terjadi karena Anda jarang menggunakan perapian, itu akan menjadi saat ketika rumah Anda terbakar.
1
@SoldOutAtivist Tidak membantu. Hal yang tidak jelas di sini (untuk seseorang yang tidak berasal dari latar belakang C) adalah perbedaan perilaku antara menyampaikan nihil eksplisit dan tidak menyampaikan apa pun, dan komentar Anda tidak menjelaskannya.
Mark Amery
Baik: Metode Obj-C apa pun yang dapat menerima sejumlah variabel dari argumen harus dihentikan secara eksplisit. Passing nothing tidak sama dengan passing nihil. Habiskan waktu dengan Obj-C dan Anda akan melihat ini berulang kali. Array bangunan menjadi yang paling umum.
3
Ini mungkin menghentikan peringatan compiler, tetapi masalah yang mendasarinya, yang dijelaskan oleh Jon Hess , masih ada - jika ada lebih dari satu penentu format myString, yang pertama akan baik-baik saja, tetapi yang kedua akan mengambil sampah dari tumpukan. Daftar substitusi NSLog()adalah tidak pernah nil -terminated, @Sold. Ada dua opsi untuk mengetahui berapa panjang daftar argumen: nilai sentinel, atau apa yang digunakan dalam printf()dan keluarga - argumen lain yang memungkinkan penghitungan nomor (misalnya, dengan menghitung penentu format).
jscs
3

Jika Anda ingin menghilangkan peringatan "format bukan string literal dan tidak ada argumen format" sekali dan untuk selamanya, Anda dapat menonaktifkan pengaturan peringatan GCC "Typecheck Calls to printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) di pengaturan build target Anda.

Aldi
sumber
5
Itu akan membungkam peringatan, tetapi tidak akan melakukan apa pun untuk memperbaiki kelemahan yang mendasari dalam aplikasi Anda. Dengan membungkam peringatan, Anda mengabaikan potensi bug yang dapat merusak aplikasi Anda hanya berdasarkan data yang dimasukkan oleh pengguna (atau dalam hal ini pesan kesalahan yang dihasilkan oleh CoreData). Akan lebih baik untuk mengikuti beberapa jawaban lain dalam pertanyaan ini untuk menghapus bug di dalam kode sumber yang menyebabkan peringatan itu muncul.
Christopher Fairbairn
2
Benar ... Itu sebabnya saya memposting "singkirkan peringatan" daripada "selesaikan".
aldi
Saya mengalami kasus di mana pustaka uthash memicu peringatan ini pada panggilan ke fungsi utstring_printf itu, jadi ini berguna dalam situasi di mana peringatan itu salah.
alfwatt
2

NSLog () mengharapkan format string, yang diteruskan hanyalah string. Anda tidak perlu menggunakan stringWithFormat :, Anda cukup melakukan:

NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])

Dan itu akan membuat peringatan itu hilang.

Elfred
sumber
2

FWIW, ini juga berlaku untuk iPhone dev. Saya melakukan pengkodean terhadap 3.1.3 SDK, dan mendapatkan kesalahan yang sama dengan masalah yang sama (bersarang stringWithFormat di dalam NSLog ()). Sixten dan Jon sedang mencari uang.

Pettiross
sumber
0

Memberi tahu siapa pun menggunakan appendFormaton NSMutableString juga dapat menyebabkan peringatan ini muncul jika mencoba meneruskan dalam string yang diformat seperti ini:

NSMutableString *csv = [NSMutableString stringWithString:@""];
NSString *csvAddition = [NSString stringWithFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];
[csv appendFormat:csvAddition];

Jadi untuk menghindari peringatan ini, ubah yang di atas menjadi ini:

NSMutableString *csv = [NSMutableString stringWithString:@""];
[csv appendFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];

Lebih ringkas dan lebih aman. Nikmati!

ColossalChris
sumber
-2
NSLog(@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]); 
ILYA2606
sumber
1
Menggunakan stringWithFormatberlebihan di sini ketika Anda bisa melakukannyaNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery