Почему переменная NSInteger должна быть приведена к long при использовании в качестве аргумента формата?

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

Код выше выдает ошибку:

Значения типа NSInteger не должны использоваться в качестве аргументов формата; вместо этого добавьте явное приведение к 'long'

Исправленное NSLogсообщение на самом деле NSLog(@"%lg", (long) myInt);. Почему мне нужно преобразовать целочисленное значение myIntв, longесли я хочу, чтобы это значение отображалось?

Дэниел Ли
источник
1
@DanielLee, если вы используете NSLog(@"%ld", (long) myInt);, longприведение в соответствие с lклассификатором %ld, но все это не нужно, так как NSLog(@"%d", myInt);достаточно (учитывая, что мы видим, что myIntэто не так long. Нижняя строка, вы приводите, myIntесли использовать длинный классификатор в формате строка, но не нужно использовать ни спецификатор формата длинной строки, ни longприведение здесь
Роб
1
Очевидно, это не правда, что NSLog (@ "% i", myInt); достаточно, потому что вы получите сообщение об ошибке, как я показал выше.
Даниэль Ли
2
@DanielLee Смотрите комментарий Мартина Р. Вы вывесили ваш вопрос с прошивкой тэгом (где NSIntegerэто не долго), но это звучит , как вы собираете с целью OS X (где NSInteger есть long ).
Роб
АА, вижу. Я не знал, что iOS и OSX сделают NSInteger разными по битам и типу.
Даниэль Ли

Ответы:

193

Вы получаете это предупреждение, если вы компилируете в OS X (64-битной), потому что на этой платформе NSIntegerопределено как longи является 64-битным целым числом. %iФормат, с другой стороны, для int, который является 32-битным. Таким образом, формат и фактический параметр не совпадают по размеру.

Так NSIntegerкак 32-битный или 64-битный, в зависимости от платформы, компилятор рекомендует добавлять приведение к longвообще.

Обновление: поскольку iOS 7 теперь также поддерживает 64-битную версию, вы можете получить то же предупреждение при компиляции для iOS.

Мартин Р
источник
1
Я получаю эту ошибку на iOS 7. Поскольку только последний iPhone 5S является 64-битным, если я установлю его так долго, это вызовет какие-либо проблемы на старых 32-битных устройствах?
Притеш Десаи
25
@BartSimpson: с явным регистром «long», как в случае NSLog(@"%ld", (long) myInt), он работает на 32-битной и 64-битной корректно.
Мартин Р
@MartinR, если мы кастуем, почему бы просто не использовать long в первую очередь?
Уильям Энтрикен
3
@FullDecent: Конечно , вы можете работать с длинными здесь: long myInt = [myNumber longValue];. Но многие (базовые) методы Foundation используют NS (U) Integer в качестве параметра или возвращаемого значения, поэтому общая проблема остается. Также в вашем приложении может иметь смысл использовать NS (U) Integer, чтобы получить больший доступный диапазон на 64-битных устройствах.
Мартин Р
39

Вам не нужно приводить что-либо, если ваши спецификаторы формата соответствуют вашим типам данных. См. Ответ Мартина Р. для получения подробной информации о том, как NSIntegerопределяется в терминах нативных типов.

Таким образом, для кода, предназначенного для построения для 64-битных сред, вы можете написать свои операторы журнала следующим образом:

NSLog(@"%ld",  myInt); 

в то время как для 32-битных сред вы можете написать:

NSLog(@"%d",  myInt); 

и все это будет работать без приведения.

В любом случае, одна из причин использования приведений заключается в том, что хороший код имеет тенденцию переноситься на разные платформы, и если вы явно приведете свои переменные, он будет скомпилирован чисто как в 32-, так и в 64-разрядных системах:

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

И обратите внимание, что это верно не только для операторов NSLog, которые, в конце концов, просто являются средствами отладки, но также для [NSString stringWithFormat:]различных производных сообщений, которые являются законными элементами производственного кода.

Monolo
источник
1
Итак, теперь, когда этот взлом требуется, все же лучше всего использовать NSInteger в первую очередь?
Уильям Энтрикен
@FullDecent Это проблема только в коде, который интерпретируется во время выполнения, таком как строки формата. Все скомпилированный код использует преимущество typeInf NSInteger.
Моноло
Это является хорошей практикой использовать NSInteger, потому что есть веские причины , почему она определяется так , как это определено.
gnasher729
22

Вместо передачи NSInteger в NSLog, просто передайте NSNumber. Это обойдет все приведения и выберет правильный спецификатор формата строки.

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

Это также работает для NSUIntegers, не беспокоясь об этом. Смотрите ответ NSInteger и NSUInteger в смешанной 64-битной / 32-битной среде

orkoden
источник
2
Я полагаю, что выбранный ответ является технически лучшим ответом на вопрос, но если вы хотите знать, как избежать передачи каждого вхождения и предотвращения предупреждений, то я считаю, что это лучшее решение.
Даниэль Вуд
0

Он сохраняет предупреждение при использовании NSLog(@"%ld", (long)myInt);, но останавливает предупреждение после объявления изменения long myInt = 1804809223;в iOS 10.

Яо Ли
источник
-2

OS X использует несколько типов данных - NSInteger, NSUInteger, CGFloat и CFIndex - для обеспечения согласованных средств представления значений в 32- и 64-разрядных средах. В 32-разрядной среде NSInteger и NSUInteger определяются как int и unsigned int соответственно. В 64-разрядных средах NSInteger и NSUInteger определяются как long и unsigned long соответственно. Чтобы избежать необходимости использовать разные спецификаторы типа printf-стиля в зависимости от платформы, вы можете использовать спецификаторы, показанные в этой ссылке, как для 32-битной, так и для 64-битной среды.

Сахеб Сингх
источник