Чому при використанні в якості аргументу формату змінну NSInteger потрібно приводити до довгої?


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

Код, наведений вище, створює помилку:

Значення типу "NSInteger" не повинні використовуватися як аргументи формату; замість цього явно додайте "довгий"

Виправлене 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-бітних пристроях?
Pritesh Desai

25
@BartSimpson: З явним випадком "long", як у NSLog(@"%ld", (long) myInt), він працює на 32-бітній і 64-бітній версії правильно.
Martin R

@MartinR, якщо ми проводимо кастинг, то чому б просто не використовувати довго в першу чергу?
Вільям Ентрікен

3
@FullDecent: Звичайно , ви можете працювати з довгими тут: long myInt = [myNumber longValue];. Але багато методів (Core) 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:]і для різних похідних повідомлень, які є законними елементами виробничого коду.


1
Тож тепер, коли цей злом необхідний, чи все-таки найкраща практика використовувати NSInteger в першу чергу?
Вільям Ентрікен

@FullDecent Це лише проблема коду, який інтерпретується під час виконання, наприклад, рядки формату. Скомпільований код Al використовує перевагу типу NSInteger.
Моноло

Це є хорошою практикою використовувати NSInteger, тому що є вагомі причини , чому вона визначається так , як це визначено.
gnasher729

22

Замість того, щоб передавати NSInteger NSLog, просто передайте NSNumber. Це дозволить обійти всі касти та вибрати правильний специфікатор формату рядка.

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

Він також працює для NSUIntegers, не переживаючи про це. Дивіться відповідь NSInteger та NSUInteger у змішаному середовищі 64 біт / 32 біт


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 визначаються як довгі та неподписані, відповідно. Щоб уникнути необхідності використовувати різні специфікатори типу printf в залежності від платформи, ви можете використовувати специфікатори, показані в цьому посиланні, як для 32-бітного, так і для 64-бітного середовища.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.