Tại sao một biến NSInteger phải được sử dụng lâu khi được sử dụng làm đối số định dạng?


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

Đoạn mã trên tạo ra lỗi:

Các giá trị của loại 'NSInteger' không nên được sử dụng làm đối số định dạng; thay vào đó, thêm một diễn viên rõ ràng vào 'dài'

Các điều chỉnh NSLogthông điệp thực sự là NSLog(@"%lg", (long) myInt);. Tại sao tôi phải chuyển đổi giá trị số nguyên myIntthành longnếu tôi muốn giá trị hiển thị?


1
@DanielLee, nếu bạn sử dụng NSLog(@"%ld", (long) myInt);, các longdiễn viên là làm cho nó phù hợp với các lvòng loại của %ld, nhưng tất cả điều đó là không cần thiết như NSLog(@"%d", myInt);là đủ (cho rằng chúng ta có thể thấy rằng myIntkhông phải là long. Tóm lại, bạn cast myIntnếu sử dụng dài vòng loại ở định dạng chuỗi, nhưng không cần sử dụng vòng loại định dạng chuỗi dài hoặc longđúc ở đây.
Rob

1
Rõ ràng, không đúng khi NSLog (@ "% i", myInt); là đủ vì bạn sẽ nhận được thông báo lỗi như tôi đã trình bày ở trên.
Daniel Lee

2
@DanielLee Xem bình luận của Martin R. Bạn đăng câu hỏi của bạn với thẻ iOS (nơi NSIntegerkhông lâu), nhưng có vẻ như bạn đang biên soạn với OS X mục tiêu (trong đó NSInteger long ).
Cướp

À, tôi hiểu rồi. Tôi không biết iOS và OSX sẽ làm cho NSInteger khác nhau về bit và loại.
Daniel Lee

Câu trả lời:


193

Bạn nhận được cảnh báo này nếu bạn biên dịch trên OS X (64 bit), bởi vì trên nền tảng đó NSIntegerđược định nghĩa là longvà là một số nguyên 64 bit. Mặt %ikhác, định dạng là dành cho int32 bit. Vì vậy, định dạng và tham số thực tế không khớp với kích thước.

NSIntegerlà 32 bit hoặc 64 bit, tùy thuộc vào nền tảng, trình biên dịch khuyên bạn nên thêm một cast vào longnói chung.

Cập nhật: Vì iOS 7 cũng hỗ trợ 64 bit, bạn có thể nhận được cảnh báo tương tự khi biên dịch cho iOS.


1
Tôi gặp lỗi này trên iOS 7. Vì chỉ iPhone 5S mới nhất là 64 bit nếu tôi cài đặt miễn là nó có gây ra sự cố nào trên các thiết bị 32 bit cũ hơn không?
Pritesh Desai

25
@BartSimpson: Với trường hợp rõ ràng là "dài", như trong NSLog(@"%ld", (long) myInt), nó hoạt động chính xác trên 32 bit và 64 bit.
Martin R

@MartinR nếu chúng ta đang casting, tại sao không sử dụng lâu ở nơi đầu tiên?
William Entriken

3
@FullDecent: Tất nhiên bạn có thể làm việc lâu dài ở đây : long myInt = [myNumber longValue];. Nhưng nhiều phương thức Foundation (Core) sử dụng NS (U) Integer làm tham số hoặc giá trị trả về, vì vậy vấn đề chung vẫn còn. Ngoài ra, có thể có ý nghĩa trong ứng dụng của bạn để sử dụng Số nguyên NS (U) để có phạm vi khả dụng lớn hơn trên các thiết bị 64 bit.
Martin R

39

Bạn không phải sử dụng bất cứ thứ gì nếu bộ định dạng định dạng khớp với kiểu dữ liệu của bạn. Xem câu trả lời của Martin R để biết chi tiết về cách NSIntegerxác định theo các loại bản địa.

Vì vậy, đối với mã dự định được xây dựng cho môi trường 64 bit, bạn có thể viết các câu lệnh nhật ký của mình như sau:

NSLog(@"%ld",  myInt); 

trong khi đối với môi trường 32 bit bạn có thể viết:

NSLog(@"%d",  myInt); 

và tất cả sẽ làm việc mà không có phôi.

Dù sao, một lý do để sử dụng phôi là mã tốt có xu hướng được chuyển qua các nền tảng và nếu bạn thực hiện các biến của mình một cách rõ ràng, nó sẽ biên dịch sạch trên cả 32 và 64 bit:

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

Và lưu ý rằng điều này đúng không chỉ với các câu lệnh NSLog, mà cuối cùng chỉ là các công cụ gỡ lỗi, mà còn cho [NSString stringWithFormat:]các thông báo dẫn xuất khác nhau, là các yếu tố hợp pháp của mã sản xuất.


1
Vì vậy, bây giờ việc hack này là bắt buộc, liệu có nên sử dụng NSInteger ngay từ đầu không?
William Entriken

@FullDecent Đây chỉ là một vấn đề trong mã được diễn giải thời gian chạy, chẳng hạn như chuỗi định dạng. Mã được biên dịch Al tận dụng lợi thế của typedef NSInteger.
Monolo

Cách tốt nhất sử dụng NSInteger, bởi vì có những lý do chính đáng tại sao nó được định nghĩa theo cách nó được định nghĩa.
gnasher729

22

Thay vì chuyển NSInteger cho NSLog, chỉ cần chuyển NSNumber. Điều này sẽ nhận được xung quanh tất cả các phôi và chọn công cụ xác định định dạng chuỗi phù hợp.

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

Nó cũng hoạt động cho NSUIntegers mà không phải lo lắng về điều đó. Xem câu trả lời cho NSInteger và NSUInteger trong môi trường hỗn hợp 64 bit / 32 bit


2
Tôi cho rằng câu trả lời được chọn về mặt kỹ thuật là câu trả lời tốt nhất cho câu hỏi, nhưng nếu bạn muốn biết làm thế nào để tránh bỏ qua mỗi lần xuất hiện và ngăn cảnh báo thì tôi thấy đây là giải pháp tốt nhất.
Daniel Wood

0

Nó tiếp tục cảnh báo trong khi sử dụng NSLog(@"%ld", (long)myInt);, nhưng dừng cảnh báo sau khi thay đổi khai báo thành long myInt = 1804809223;trong iOS 10.


-2

OS X sử dụng một số loại dữ liệu, NSInteger, NSUInteger, CGFloat và CFIndex, để cung cấp một phương tiện nhất quán để biểu diễn các giá trị trong môi trường 32 và 64 bit. Trong môi trường 32 bit, NSInteger và NSUInteger được định nghĩa lần lượt là int và unsign int. Trong môi trường 64 bit, NSInteger và NSUInteger được định nghĩa lần lượt là dài và không dấu. Để tránh phải sử dụng các công cụ xác định kiểu printf khác nhau tùy thuộc vào nền tảng, bạn có thể sử dụng các công cụ xác định được hiển thị trong liên kết này cho cả môi trường 32 bit và 64 bit.

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.