Cảnh báo: "định dạng không phải là một chuỗi theo nghĩa đen và không có đối số định dạng"


110

Kể từ khi nâng cấp lên Xcode 3.2.1 và Snow Leopard mới nhất, tôi đã nhận được cảnh báo

"định dạng không phải là một chuỗi ký tự và không có đối số định dạng"

từ mã sau:

NSError *error = nil;

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

}

Nếu errorMsgFormatlà một NSStringvới các chỉ định định dạng (ví dụ "print me like this: %@":), điều gì sai với lệnh NSLoggọi trên ? Và cách nào được khuyến nghị để khắc phục để cảnh báo không được tạo ra?

Câu trả lời:


113

Bạn có lồng các dấu ngoặc của mình một cách chính xác không? Tôi không nghĩ chỉ NSLog()thích lấy một đối số, đó là những gì bạn đang vượt qua nó. Ngoài ra, nó đã thực hiện định dạng cho bạn. Tại sao không chỉ làm điều này?

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

Hoặc, vì bạn nói errorMsgFormatlà một chuỗi định dạng với một trình giữ chỗ duy nhất, bạn đang cố gắng thực hiện điều này?

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

14
"Tôi không nghĩ rằng NSLog () chỉ thích lấy một đối số" NSLog()có thể lấy một đối số, khi chuỗi định dạng không chứa mã định dạng.
user102008

Đưa ra cảnh báo khác Đối số dữ liệu không được sử dụng bởi chuỗi định dạng.
hasan

157

Xcode đang phàn nàn vì đây là một vấn đề bảo mật.

Đây là mã tương tự như mã của bạn:

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

Câu lệnh NSLog cuối cùng đó sẽ thực thi tương đương như sau:

NSLog(@"Jon Hess %@");

Điều đó sẽ khiến NSLog tìm kiếm thêm một đối số chuỗi, nhưng không có. Do cách thức hoạt động của ngôn ngữ C, nó sẽ chọn một số con trỏ rác ngẫu nhiên từ ngăn xếp và cố gắng xử lý nó như một NSString. Điều này rất có thể sẽ làm hỏng chương trình của bạn. Bây giờ các chuỗi của bạn có thể không có% @ s trong đó, nhưng một ngày nào đó chúng có thể xảy ra. Bạn nên luôn sử dụng chuỗi định dạng với dữ liệu mà bạn kiểm soát rõ ràng làm đối số đầu tiên cho các hàm lấy chuỗi định dạng (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Như Otto đã chỉ ra, có lẽ bạn chỉ nên làm một số việc như:

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

17
Và một lần nữa trên SO, các câu trả lời chi tiết và hay lại nằm ở khía cạnh khác. CẢM ƠN BẠN đã giải thích điều này đầy đủ. Tôi sẽ không bao giờ hiểu ra điều này.
Dan Rosenstark

38

Câu trả lời cuối cùng: Như Jon Hess đã nói, đó là một vấn đề bảo mật vì bạn đang truyền một chuỗi WHATEVER cho một hàm mong đợi một chuỗi định dạng. Tức là, nó sẽ đánh giá tất cả các chỉ định định dạng TRONG VÒNG bất kỳ chuỗi nào. Nếu không có bất kỳ, tuyệt vời, nhưng nếu có, những điều tồi tệ có thể xảy ra.

Sau đó, điều thích hợp cần làm là SỬ DỤNG một chuỗi định dạng, ví dụ:

NSLog(@"%@", myNSString);

Bằng cách đó, ngay cả khi có các chỉ định định dạng trong myNSString, chúng sẽ không được NSLog đánh giá.


13

Tôi không đặc biệt khuyên bạn nên sử dụng điều này, vì cảnh báo LÀ một cảnh báo thực sự .. trong một cách sử dụng động của ngôn ngữ, có thể thực hiện mọi thứ trong thời gian chạy đối với chuỗi (tức là chèn thông tin mới hoặc thậm chí làm hỏng chương trình) .. Tuy nhiên, có thể buộc phải đàn áp nếu bạn BIẾT rằng nó phải như thế này và bạn thực sự không muốn được cảnh báo về điều đó ..

#pragma GCC diagnostic ignored "-Wformat-security"

Sẽ yêu cầu GCC tạm thời bỏ qua cảnh báo biên dịch .. Một lần nữa, nó không giải quyết được gì nhưng có thể đôi khi bạn không thể tìm ra cách tốt để thực sự khắc phục sự cố.

CHỈNH SỬA: Kể từ tiếng kêu, pragma đã thay đổi. Xem cái này: https://stackoverflow.com/a/17322337/3937


10

Cách nhanh nhất để khắc phục nó sẽ là thêm @"%@",làm đối số đầu tiên cho NSLoglệnh gọi của bạn , tức là

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

Tuy nhiên, có lẽ bạn nên xem xét câu trả lời của Sixteen Otto.


10

Tôi vừa vượt qua con số không để phủ nhận các cảnh báo, có lẽ điều đó sẽ hiệu quả với bạn?

NSLog (myString, nil);


5
Ai đó có thể giải thích TẠI SAO vượt qua nil khi paramente thứ hai giải quyết cảnh báo không?
cprcrack

1
Truyền nil là rõ ràng trong khi thiếu tham số thứ hai thì không. Bạn có thể cho rằng lò sưởi của bạn không được thắp sáng khi bạn rời khỏi nhà hoặc bạn có thể chắc chắn rằng nó không được thắp sáng. Mặc dù không có gì thường xảy ra vì bạn hiếm khi sử dụng lò sưởi của mình, nhưng sẽ có lúc ngôi nhà của bạn bị cháy rụi.

1
@SoldOutActivist Không hữu ích. Điểm không rõ ràng ở đây (đối với một người không xuất thân từ nền tảng C) là sự khác biệt trong hành vi giữa việc chuyển một số nil rõ ràng và không chuyển bất cứ điều gì và nhận xét của bạn không giải thích điều đó.
Mark Amery

Tốt: Bất kỳ phương thức obj-C nào có thể chấp nhận một số lượng biến đối số phải được kết thúc rõ ràng là nil. Không có gì vượt qua không giống như vượt qua con số không. Hãy dành bất kỳ thời gian nào với Obj-C và bạn sẽ thấy điều này lặp đi lặp lại. Mảng tòa nhà là phổ biến nhất.

3
Điều này có thể dừng cảnh báo của trình biên dịch, nhưng vấn đề cơ bản, được Jon Hess giải thích , vẫn tồn tại - nếu có nhiều hơn một định dạng định dạng trong đó myString, định dạng đầu tiên sẽ ổn, nhưng định dạng thứ hai sẽ nhặt rác khỏi ngăn xếp. Danh sách thay thế trong NSLog()không bao giờ nil chấm dứt, @Sold. Có hai tùy chọn để tìm ra danh sách các đối số dài bao nhiêu: giá trị sentinel hoặc những gì được sử dụng trong printf()và family - một đối số khác cho phép tính toán số lượng (ví dụ: bằng cách đếm các chỉ định định dạng).
jscs

3

Nếu bạn muốn loại bỏ cảnh báo "định dạng không phải là chuỗi theo nghĩa đen và không có đối số định dạng" một lần và mãi mãi, bạn có thể tắt cài đặt cảnh báo GCC "Cuộc gọi kiểu chữ đến printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) trong cài đặt bản dựng của mục tiêu.


5
Điều đó sẽ tắt cảnh báo, nhưng nó sẽ không làm bất cứ điều gì để sửa lỗi cơ bản trong ứng dụng của bạn. Bằng cách tắt tiếng cảnh báo, bạn đang bỏ qua một lỗi tiềm ẩn có thể làm hỏng ứng dụng của bạn chỉ dựa trên dữ liệu do người dùng nhập (hoặc trong trường hợp này là thông báo lỗi do CoreData tạo ra). Sẽ tốt hơn nếu bạn làm theo một số câu trả lời khác trong câu hỏi này để loại bỏ lỗi trong mã nguồn khiến cảnh báo xuất hiện.
Christopher Fairbairn

2
Đúng ... Đó là lý do tại sao tôi đăng "thoát khỏi cảnh báo" thay vì "giải quyết".
aldi

Tôi đã gặp phải trường hợp thư viện uthash đang kích hoạt cảnh báo này khi gọi đến hàm utstring_printf của nó, vì vậy điều này hữu ích trong các tình huống cảnh báo sai.
alfwatt

2

NSLog () mong đợi một chuỗi định dạng, những gì được truyền vào chỉ là một chuỗi. Bạn không cần sử dụng stringWithFormat :, bạn chỉ có thể làm:

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

Và điều đó sẽ khiến cảnh báo biến mất.


2

FWIW, điều này cũng áp dụng cho nhà phát triển iPhone. Tôi đang viết mã theo SDK 3.1.3 và gặp lỗi tương tự với cùng một vấn đề (lồng chuỗiWithFormat bên trong NSLog ()). Sixten và Jon đang trên tiền.


0

Chỉ cho bất kỳ ai biết bằng cách sử dụng appendFormattrên NSMutableString cũng có thể khiến cảnh báo này xuất hiện nếu cố gắng chuyển vào một chuỗi được định dạng như vậy:

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

Vì vậy, để tránh cảnh báo này, hãy chuyển những điều trên thành thế này:

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

Ngắn gọn hơn và an toàn hơn. Thưởng thức!


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

1
Sử dụng stringWithFormatlà không cần thiết ở đây khi bạn chỉ có thể làmNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Đánh dấu Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.