Когда использовать NSInteger против Int

344

Когда я должен использовать NSIntegerпротив INT при разработке для iOS? Я вижу в примере кода Apple, который они используют NSInteger(или NSUInteger), когда передают значение в качестве аргумента функции или возвращают значение из функции.

- (NSInteger)someFunc;...
- (void)someFuncWithInt:(NSInteger)value;...

Но внутри функции, которую они просто используют intдля отслеживания значения

for (int i; i < something; i++)
...

int something;
something += somethingElseThatsAnInt;
...

Я читал (мне сказали), что NSIntegerэто безопасный способ ссылаться на целое число в 64-битной или 32-битной среде, так зачем intвообще его использовать ?

Shizam
источник

Ответы:

322

Обычно вы хотите использовать, NSIntegerкогда вы не знаете, на какой архитектуре процессора может работать ваш код, поэтому вы можете по какой-то причине захотеть использовать максимально возможный целочисленный тип, который в 32-битных системах является простым int, тогда как в 64-битном Система это long.

Я бы придерживался использования NSIntegerвместо int/, longесли вы специально не нуждаетесь в них.

NSInteger/ NSUIntegerопределены как * динамические typedef* для одного из этих типов, и они определены следующим образом:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

Что касается правильного спецификатора формата, который вы должны использовать для каждого из этих типов, см. Раздел «Руководство по программированию строки» в разделе «Зависимости платформы».

Джейкоб Релкин
источник
4
Кроме того, я бы сказал, что лучше использовать NSInteger, если вам не требуется int или long int.
v01d
4
@Shizam Возможно, что использование intлучше подойдет даже для long. Может быть, вы знаете, что он не превысит определенный диапазон, и поэтому думаете, что он будет более эффективным в использовании памяти int.
Джейкоб Релкин
58
Я не согласен с этим ответом. Единственное, что я использовал бы NSIntegerдля передачи значений и из API, который его определяет. Кроме этого он не имеет никакого преимущества перед int или long. По крайней мере с int или long вы знаете, какие спецификаторы формата использовать в printf или аналогичном выражении.
JeremyP
3
Что произойдет, если вам нужно хранить long, и вы используете NSInteger, когда вы работаете в системе 64b, но другой пользователь использует систему 32b? Вы не заметите сбой, но пользователь заметит.
arielcamus
14
Это задом наперед. Всегда используйте int, если у вас нет другой причины. Использование специфичных для платформы определений для простых целых чисел только затрудняет чтение вашего кода.
Гленн Мейнард
44

Зачем intвообще использовать ?

Apple использует intпотому, что для переменной управления циклом (которая используется только для управления итерациями цикла) intтип данных хорош, как по размеру типа данных, так и по значениям, которые он может содержать для вашего цикла. Здесь не требуется тип данных, зависящий от платформы. Для переменной управления циклом intбольшую часть времени будет выполнять даже 16-битная переменная .

Apple использует NSIntegerвозвращаемое значение функции или аргумент функции, потому что в этом случае тип данных [размер] имеет значение , потому что то, что вы делаете с функцией, это передача / передача данных с другими программами или с другими частями кода; см. ответ на вопрос Когда я должен использовать NSInteger против INT? в самом вашем вопросе ...

они [Apple] используют NSInteger (или NSUInteger) при передаче значения в качестве аргумента функции или возвращении значения из функции.

Только ты
источник
32

OS X является "LP64". Это означает, что:

int всегда 32-битный

long long всегда 64-битный

NSIntegerи longвсегда размером с указатель. Это означает, что они 32-разрядные в 32-разрядных системах и 64-разрядные в 64-разрядных системах.

Причина, по которой NSInteger существует, заключается в том, что многие устаревшие API неправильно используются intвместо того, longчтобы хранить переменные размером с указатель, что означало, что API пришлось менять с intна long64-битные версии. Другими словами, API будет иметь разные сигнатуры функций в зависимости от того, компилируете ли вы для 32-битной или 64-битной архитектуры. NSIntegerнамерен замаскировать эту проблему с помощью этих устаревших API.

В своем новом коде используйте, intесли вам нужна 32-битная переменная, long longесли вам нужно 64-битное целое число, longили NSIntegerесли вам нужна переменная размером с указатель.

Даррен
источник
25
История на месте, но совет ужасен. Если вам нужна 32-битная переменная, используйте int32_t. Если вам нужно 64-битное целое число, используйте int64_t. Если вам нужна переменная размером с указатель, используйте intptr_t.
Стивен Кэнон
5
Стивен, ваш совет: никогда не использовать int, long или NSInteger?
Даррен
7
Нет, я советую никогда не использовать их, если вам требуется целочисленный тип известного фиксированного размера. Эти <stdint.h>типы существуют для этой цели.
Стивен Кэнон
3
Стивен, мой ответ был в ответ на вопрос «Когда использовать NSInteger vs int», а не «что такое кросс-платформенное имя типа 32-разрядного целого числа». Если кто-то пытается выбрать между NSInteger и int, он также может знать, насколько велики они на платформах, которые он поддерживает.
Даррен
1
Обратите внимание, что LP64это не гарантирует, что long longэто 64 бит. LP64Платформа может избрать , чтобы long longбыть 128 - битное целое.
Стивен Кэнон
26

Если вы копаетесь в реализации NSInteger:

#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif

Проще говоря, typedef NSInteger делает шаг за вас: если архитектура 32-битная, она использует int, если она 64-битная, она использует long. Используя NSInteger, вам не нужно беспокоиться об архитектуре, на которой работает программа.

Эван Мулавски
источник
14
Вы должны беспокоиться, потому что правильный спецификатор формата для NSInteger зависит от архитектуры.
JeremyP
Согласно руководству Apple, самый простой способ - привести значение к наибольшему числовому типу long long. Таким образом, все числовые типы будут использовать один и тот же спецификатор типа.
Eonil
6
Теперь самый простой способ отформатировать это просто боксировать -NSLog("%@", @(1123));
Eonil
1
Вы также можете разыграть его:NSLog("%li", (long)theNSInteger);
Даниил
кастинг меня огорчает
tomalbrc
9

Вы должны использовать NSIntegers, если вам нужно сравнить их с постоянными значениями, такими как NSNotFound или NSIntegerMax, так как эти значения будут отличаться в 32-разрядных и 64-разрядных системах, поэтому значения индекса, числа и т. П .: используйте NSInteger или NSUInteger.

Использование NSInteger в большинстве случаев не повредит, за исключением того, что он занимает вдвое больше памяти. Влияние на память очень мало, но если у вас есть огромное количество чисел, плавающих в одно время, это может иметь значение для использования целых чисел.

Если вы используете NSInteger или NSUInteger, при использовании строк форматирования вы захотите привести их к длинным целым или длинным целым без знака, так как новая функция Xcode возвращает предупреждение, если вы попытаетесь выйти из NSInteger так, как если бы он имел известную длину. Точно так же вы должны быть осторожны при отправке их переменным или аргументам, которые вводятся как целые, так как вы можете потерять некоторую точность в процессе.

В целом, если вы не ожидаете, что сотни тысяч их будут в памяти одновременно, проще использовать NSInteger, чем постоянно беспокоиться о разнице между ними.

ясень
источник
9

На данный момент (сентябрь 2014 г.) я бы рекомендовал использовать NSInteger/CGFloatпри взаимодействии с iOS API и т. Д., Если вы также создаете свое приложение для arm64. Это потому , что вы, вероятно , получите неожиданные результаты при использовании float, longи intтипов.

ПРИМЕР: FLOAT / DOUBLE против CGFLOAT

В качестве примера мы берем метод делегата UITableView tableView:heightForRowAtIndexPath:.

В 32-битном приложении оно будет работать нормально, если оно написано так:

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

floatэто 32-битное значение, а возвращаемое вами 44 - это 32-битное значение. Однако, если мы скомпилируем / запустим этот же кусок кода в 64-битной архитектуре arm64, 44 будет 64-битным значением. Возвращение 64-битного значения, когда ожидается 32-битное значение, даст неожиданную высоту строки.

Вы можете решить эту проблему, используя CGFloatтип

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

Этот тип представляет 32-разрядную floatв 32-разрядной среде и 64-разрядную doubleв 64-разрядной среде. Поэтому при использовании этого типа метод всегда будет получать ожидаемый тип независимо от среды компиляции / среды выполнения.

То же самое верно для методов, которые ожидают целые числа. Такие методы ожидают 32-битное intзначение в 32-битной среде и 64-битное longв 64-битной среде. Вы можете решить этот случай, используя тип, NSIntegerкоторый служит как intили longоснованный на environemnt времени компиляции / выполнения.

Леон Лукарди
источник
Что если я знаю, что значение этой конкретной переменной не может содержать больших числовых значений, и поэтому я хочу использовать int. Будет ли это нормально работать как в 64-битной среде? Я думаю, что это должно произойти, так как я не видел цикл for, подобный этому: for (int i = 0; i <10; i ++) делает неправильное поведение независимо от среды, в которой он запущен.
Chanchal Raj
@Chanchal Raj До тех пор, пока нет преобразования или преобразования в другие типы или использования / переопределения сторонних классов и методов с этой переменной, использование int вместо NSInteger будет приемлемым.
Леон Лукарди
9

На iOS это в настоящее время не имеет значения, используете ли вы intили NSInteger. Это будет иметь большее значение, если / когда iOS перейдет на 64-битную версию.

Проще говоря, NSIntegers - это int32-битный код (и, следовательно, 32-битный код), а longs - 64-битный код ( longs в 64-битном коде имеют ширину 64 бит, но 32-битный в 32-битном коде). Наиболее вероятная причина использования NSIntegerвместо этого longсостоит в том, чтобы не нарушать существующий 32-битный код (который использует ints).

CGFloatимеет ту же проблему: на 32-битной (по крайней мере на OS X), это float; на 64-битной, это так double.

Обновление: с появлением iPhone 5s, iPad Air, iPad Mini с Retina и iOS 7 теперь вы можете создавать 64-битный код на iOS.

Обновление 2: также использование NSIntegers помогает в совместимости кода Swift.

MaddTheSane
источник
0

int = 4 байта (фиксированный независимо от размера архитектора) NSInteger = зависит от размера архитектора (например, для 4-байтового архитектора = 4 байта размера NSInteger)

Анкит Гарг
источник