คำเตือน:“ format not a string literal and no format arguments”


110

ตั้งแต่อัปเกรดเป็น Xcode 3.2.1 และ Snow Leopard ล่าสุดฉันได้รับคำเตือน

"format ไม่ใช่สตริงลิเทอรัลและไม่มีอาร์กิวเมนต์รูปแบบ"

จากรหัสต่อไปนี้:

NSError *error = nil;

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

}

ถ้าerrorMsgFormatเป็นNSStringกับตัวระบุรูปแบบ (เช่น :) "print me like this: %@"มีอะไรผิดปกติกับการNSLogเรียกข้างต้น? และวิธีใดที่แนะนำในการแก้ไขเพื่อไม่ให้เกิดคำเตือน

คำตอบ:


113

คุณวางวงเล็บของคุณถูกต้องหรือไม่? ฉันไม่คิดว่าNSLog()ชอบการโต้เถียงเพียงเรื่องเดียวซึ่งเป็นสิ่งที่คุณผ่านมันไป นอกจากนี้ยังจัดรูปแบบให้คุณอยู่แล้ว ทำไมไม่ทำแบบนี้

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

หรือเนื่องจากคุณบอกว่าerrorMsgFormatเป็นสตริงรูปแบบที่มีตัวยึดตำแหน่งเดียวคุณกำลังพยายามทำสิ่งนี้หรือไม่?

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

14
"ฉันไม่คิดว่า NSLog () ชอบรับอาร์กิวเมนต์เดียว" NSLog()สามารถใช้อาร์กิวเมนต์เดียวเมื่อสตริงรูปแบบไม่มีตัวระบุรูปแบบ
user102008

ให้คำเตือนอาร์กิวเมนต์ข้อมูลที่ไม่ได้ใช้โดยสตริงรูปแบบ
hasan

157

Xcode กำลังบ่นเนื่องจากนี่เป็นปัญหาด้านความปลอดภัย

นี่คือรหัสที่คล้ายกับของคุณ:

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

คำสั่ง NSLog สุดท้ายจะดำเนินการเทียบเท่ากับสิ่งนี้:

NSLog(@"Jon Hess %@");

นั่นจะทำให้ NSLog มองหาอาร์กิวเมนต์สตริงอีกหนึ่งชุด แต่ไม่มีเลย เนื่องจากวิธีการทำงานของภาษา C มันจะหยิบตัวชี้ขยะแบบสุ่มจากสแต็กและพยายามปฏิบัติเหมือน NSString ซึ่งมักจะทำให้โปรแกรมของคุณขัดข้อง ตอนนี้สตริงของคุณอาจไม่มี% @ อยู่ในนั้น แต่สักวันก็อาจ คุณควรใช้สตริงรูปแบบกับข้อมูลที่คุณควบคุมอย่างชัดเจนเป็นอาร์กิวเมนต์แรกของฟังก์ชันที่ใช้สตริงรูปแบบ (printf, scanf, NSLog, - [NSString stringWithFormat:], ... )

ดังที่ Otto ชี้ให้เห็นคุณควรทำบางสิ่งเช่น:

NSLog(errorMsgFormat, error, [error userInfo]);

17
และอีกครั้งใน SO คำตอบที่ละเอียดและดีตกอยู่ข้างทาง ขอขอบคุณที่อธิบายเรื่องนี้อย่างเต็มที่ ฉันจะไม่คิดออก
Dan Rosenstark

38

คำตอบสุดท้าย: ดังที่ Jon Hess กล่าวว่าเป็นปัญหาด้านความปลอดภัยเนื่องจากคุณกำลังส่งสตริง WHATEVER ไปยังฟังก์ชันที่คาดว่าจะมีสตริงรูปแบบ นั่นคือมันจะประเมินตัวระบุรูปแบบทั้งหมดภายในสตริงใด ๆ ถ้าไม่มีก็น่ากลัว แต่ถ้ามีสิ่งเลวร้ายอาจเกิดขึ้นได้

สิ่งที่ควรทำคือใช้สตริงรูปแบบโดยตรงตัวอย่างเช่น

NSLog(@"%@", myNSString);

ด้วยวิธีนี้แม้ว่าจะมีตัวระบุรูปแบบใน myNSString แต่ก็ไม่ได้รับการประเมินโดย NSLog


13

ฉันไม่แนะนำให้ใช้สิ่งนี้เป็นพิเศษเนื่องจากคำเตือนเป็นคำเตือนจริง .. ในการใช้ภาษาแบบไดนามิกจึงเป็นไปได้ที่จะทำสิ่งต่างๆรันไทม์กับสตริง (เช่นแทรกข้อมูลใหม่หรือแม้แต่ทำให้โปรแกรมหยุดทำงาน) .. อย่างไรก็ตามเป็นไปได้ บังคับปราบปรามถ้าคุณรู้ว่ามันควรจะเป็นแบบนี้และคุณไม่อยากถูกเตือนจริงๆ ..

#pragma GCC diagnostic ignored "-Wformat-security"

จะบอกให้ GCC เพิกเฉยต่อคำเตือนการรวบรวมชั่วคราว .. อีกครั้งมันไม่ได้แก้ปัญหาอะไรเลย แต่อาจมีบางครั้งที่คุณไม่สามารถหาวิธีที่ดีในการแก้ไขปัญหาได้จริง

แก้ไข: เมื่อเสียงดังขึ้น pragma มีการเปลี่ยนแปลง ดูสิ่งนี้: https://stackoverflow.com/a/17322337/3937


10

วิธีที่เร็วที่สุดในการแก้ไขคือเพิ่ม@"%@",อาร์กิวเมนต์แรกในการNSLogโทรของคุณนั่นคือ

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

อย่างไรก็ตามคุณควรพิจารณาคำตอบของอ็อตโตสิบหก


10

ฉันเพิ่งผ่านศูนย์เพื่อลบล้างคำเตือนนั่นอาจจะเหมาะกับคุณ?

NSLog (myString ศูนย์);


5
มีใครอธิบายได้ไหมว่าทำไมการผ่านศูนย์ในฐานะที่สองจึงแก้คำเตือนได้
cprcrack

1
การส่งผ่านศูนย์นั้นชัดเจนในขณะที่ไม่มีพารามิเตอร์ที่สอง คุณสามารถสมมติว่าเตาผิงของคุณไม่สว่างเมื่อคุณออกจากบ้านหรือตรวจสอบให้แน่ใจว่าไม่ได้ แม้ว่าปกติแล้วจะไม่มีอะไรเกิดขึ้นเนื่องจากคุณแทบไม่ได้ใช้เตาผิง แต่ก็มีอยู่ครั้งหนึ่งเมื่อบ้านของคุณไฟไหม้

1
@SoldOutActivist ไม่ช่วยเหลือ. จุดที่ไม่ชัดเจนที่นี่ (สำหรับคนที่ไม่ได้มาจากพื้นหลัง C) คือความแตกต่างของพฤติกรรมระหว่างการส่งผ่านศูนย์ที่ชัดเจนและไม่ผ่านอะไรและความคิดเห็นของคุณไม่ได้อธิบายสิ่งนั้น
Mark Amery

ดี: เมธอด Obj-C ใด ๆ ที่สามารถยอมรับจำนวนอาร์กิวเมนต์ที่แปรผันจะต้องถูกยกเลิกอย่างชัดเจน การผ่านอะไรที่ไม่เหมือนกับการผ่านศูนย์ ใช้เวลากับ Obj-C เท่าไหร่ก็ได้แล้วคุณจะเห็นสิ่งนี้ซ้ำแล้วซ้ำเล่า การสร้างอาร์เรย์เป็นเรื่องปกติมากที่สุด

3
สิ่งนี้อาจหยุดคำเตือนของคอมไพลเลอร์ แต่ปัญหาพื้นฐานซึ่งอธิบายโดย Jon Hessยังคงมีอยู่ - หากมีตัวระบุรูปแบบมากกว่าหนึ่งmyStringตัวตัวแรกจะไม่เป็นไร แต่อันที่สองจะเก็บขยะออกจากสแต็ก รายการเปลี่ยนตัวในNSLog()จะไม่ ถูกnilตัดทอน @Sold มีสองตัวเลือกในการหาว่ารายการอาร์กิวเมนต์มีความยาวเท่าใด: ค่า Sentinel หรือสิ่งที่ใช้ในprintf()และตระกูล - อาร์กิวเมนต์อื่นที่อนุญาตให้คำนวณจำนวน (เช่นโดยการนับตัวระบุรูปแบบ)
jscs

3

หากคุณต้องการกำจัดคำเตือน "รูปแบบไม่ใช่สตริงลิเทอรัลและไม่มีอาร์กิวเมนต์รูปแบบ" เพียงครั้งเดียวคุณสามารถปิดใช้งานการตั้งค่าคำเตือน GCC "Typecheck Calls to printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) ในการตั้งค่าบิลด์ของเป้าหมาย


5
ซึ่งจะปิดเสียงเตือน แต่จะไม่ทำอะไรเพื่อแก้ไขข้อบกพร่องที่อยู่ภายในแอปพลิเคชันของคุณ โดยการปิดเสียงเตือนคุณจะเพิกเฉยต่อข้อบกพร่องที่อาจเกิดขึ้นซึ่งอาจทำให้แอปพลิเคชันของคุณขัดข้องตามข้อมูลที่ป้อนโดยผู้ใช้ (หรือในกรณีนี้คือข้อความแสดงข้อผิดพลาดที่สร้างโดย CoreData) จะเป็นการดีกว่าหากทำตามคำตอบอื่น ๆ ในคำถามนี้เพื่อลบข้อบกพร่องภายในซอร์สโค้ดที่ทำให้คำเตือนปรากฏขึ้น
Christopher Fairbairn

2
จริง ... นั่นคือเหตุผลที่ฉันโพสต์ "กำจัดคำเตือน" แทนที่จะเป็น "แก้"
aldi

ฉันพบในกรณีที่ไลบรารี uthash เรียกคำเตือนนี้เมื่อมีการเรียกไปยังฟังก์ชัน utstring_printf ดังนั้นสิ่งนี้จึงมีประโยชน์ในสถานการณ์ที่คำเตือนไม่ถูกต้อง
alfwatt

2

NSLog () คาดหวังสตริงรูปแบบสิ่งที่ส่งผ่านเป็นเพียงสตริง คุณไม่จำเป็นต้องใช้ stringWithFormat: คุณสามารถทำได้:

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

และนั่นจะทำให้คำเตือนหายไป


2

FWIW สิ่งนี้ใช้ได้กับ iPhone dev เช่นกัน ฉันกำลังเข้ารหัสกับ 3.1.3 SDK และมีข้อผิดพลาดเดียวกันกับปัญหาเดียวกัน (การซ้อน stringWithFormat ภายใน NSLog ()) Sixten และ Jon อยู่ในเงิน


0

การแจ้งให้ทุกคนทราบโดยใช้appendFormatบน NSMutableString ยังสามารถทำให้คำเตือนนี้ปรากฏขึ้นหากพยายามส่งผ่านสตริงที่จัดรูปแบบดังนี้:

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

ดังนั้นเพื่อหลีกเลี่ยงคำเตือนนี้ให้เปลี่ยนด้านบนเป็นสิ่งนี้:

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

กระชับและปลอดภัยมากขึ้น สนุก!


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

1
การใช้stringWithFormatซ้ำซ้อนที่นี่เมื่อคุณทำได้NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.