Quando usar o NSInteger vs. int

344

Quando devo usar NSIntegervs. int ao desenvolver para iOS? Vejo no código de exemplo da Apple que eles usam NSInteger(ou NSUInteger) ao passar um valor como argumento para uma função ou retornar um valor de uma função.

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

Mas dentro de uma função que eles estão usando intpara rastrear um valor

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

int something;
something += somethingElseThatsAnInt;
...

Eu li (me disseram) que NSIntegeré uma maneira segura de fazer referência a um número inteiro em um ambiente de 64 ou 32 bits, então por que usar int?

Shizam
fonte

Respostas:

322

Você geralmente deseja usar NSIntegerquando não sabe em que tipo de arquitetura de processador seu código pode ser executado, portanto, por algum motivo, deseja o maior tipo inteiro possível, que em sistemas de 32 bits é apenas um int, enquanto em um de 64 bits. sistema é um long.

Eu continuaria usando em NSIntegervez de int/ a longmenos que você os requeira especificamente.

NSInteger/ NSUIntegersão definidos como * dynamic typedef* s para um desses tipos e são definidos assim:

#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

Com relação ao especificador de formato correto que você deve usar para cada um desses tipos, consulte a seção do Guia de programação de strings em Dependências da plataforma

Jacob Relkin
fonte
4
Além disso, eu diria que é melhor usar o NSInteger, a menos que você precise especificamente de int ou long int.
v01d 14/12/10
4
@Shizam É possível que usar um intseja mais adequado para até um long. Talvez você saiba que não excederá um determinado intervalo e, portanto, acha que será mais eficiente em termos de memória simplesmente usar int.
Jacob Relkin
58
Eu discordo desta resposta. A única coisa que eu usaria NSIntegeré passar valores de e para uma API que a especifica. Fora isso, não tem vantagem sobre um int ou um longo. Pelo menos com um int ou um longo, você sabe quais especificadores de formato usar em uma declaração printf ou similar.
JeremyP
3
O que acontece se você precisar armazenar um arquivo longo e usar o NSInteger enquanto estiver trabalhando em um sistema 64b, mas outro usuário usar um sistema 32b? Você não notará a falha, mas o usuário notará.
Arielcamus
14
Isso é ao contrário. Sempre use int, a menos que você tenha um motivo específico. O uso de definições específicas da plataforma para números inteiros simples não faz nada, mas torna seu código mais difícil de ler.
Glenn Maynard
44

Por que usar int?

A Apple usa intporque, para uma variável de controle de loop (que é usada apenas para controlar as iterações do loop), o inttipo de dados é bom, tanto no tamanho do tipo de dados quanto nos valores que ele pode conter para o loop. Não há necessidade de tipo de dados dependente da plataforma aqui. Para uma variável de controle de loop, mesmo um de 16 bits intfará a maior parte do tempo.

A Apple usa NSIntegerpara um valor de retorno de função ou para um argumento de função, porque nesse caso o tipo de dados [tamanho] é importante , porque o que você está fazendo com uma função é comunicar / transmitir dados com outros programas ou com outros trechos de código; veja a resposta para Quando devo usar o NSInteger vs int? na sua própria pergunta ...

eles [Apple] usam NSInteger (ou NSUInteger) ao passar um valor como argumento para uma função ou retornar um valor de uma função.

Só você
fonte
32

OS X é "LP64". Isso significa que:

int é sempre de 32 bits.

long long é sempre de 64 bits.

NSIntegere longsão sempre do tamanho de ponteiros. Isso significa que eles são de 32 bits em sistemas de 32 bits e 64 bits em sistemas de 64 bits.

O motivo pelo qual o NSInteger existe é que muitas APIs herdadas foram usadas incorretamente int vez de longconter variáveis ​​do tamanho de ponteiros, o que significava que as APIs precisavam mudar de intpara longnas versões de 64 bits. Em outras palavras, uma API teria assinaturas de funções diferentes, dependendo da compilação de arquiteturas de 32 ou 64 bits. NSIntegerpretende mascarar esse problema com essas APIs herdadas.

Em seu novo código, o uso intse você precisa de uma variável de 32 bits, long longse você precisa de um número inteiro de 64-bit, e longou NSIntegerse você precisar de uma variável ponteiro porte.

Darren
fonte
25
A história está no local, mas o conselho é terrível. Se você precisar de uma variável de 32 bits, use int32_t. Se você precisar de um número inteiro de 64 bits int64_t. Se você precisar de uma variável do tamanho de um ponteiro, use intptr_t.
Stephen Canon
5
Stephen, seu conselho é nunca usar int, long ou NSInteger então?
Darren
7
Não, meu conselho é nunca usá-los se você precisar de um tipo inteiro de tamanho fixo conhecido. Os <stdint.h>tipos existem para esse fim.
Stephen Canon
3
Stephen, minha resposta foi em resposta à pergunta "Quando usar o NSInteger vs int", e não "qual é o nome do tipo de plataforma cruzada de um número inteiro de 32 bits". Se alguém está tentando decidir entre o NSInteger e o int, ele também deve saber o tamanho que tem nas plataformas suportadas.
Darren
11
Observe também que LP64não garante que long longsejam 64 bits. Uma LP64plataforma pode optar por ter long longum número inteiro de 128 bits.
Stephen Canon
26

Se você se interessar pela implementação do NSInteger:

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

Simplesmente, o NSInteger typedef faz um passo para você: se a arquitetura é de 32 bits, usa int, se é de 64 bits, usa long. Usando o NSInteger, você não precisa se preocupar com a arquitetura em que o programa está sendo executado.

Evan Mulawski
fonte
14
Você precisa se preocupar, porque o especificador de formato correto para o NSInteger depende da arquitetura.
JeremyP
A maneira mais simples, de acordo com o manual da Apple, é converter o valor para o maior tipo numérico long long. Portanto, todos os tipos numéricos usarão o mesmo especificador de tipo.
Eonil
6
Agora, a maneira mais simples de formatar é apenas encaixotá-losNSLog("%@", @(1123));
#
11
você também pode lançá-lo:NSLog("%li", (long)theNSInteger);
Daniel
fundição me deixa triste
tomalbrc
9

Você deve usar o NSIntegers se precisar compará-los com valores constantes, como NSNotFound ou NSIntegerMax, pois esses valores diferem nos sistemas de 32 e 64 bits, portanto, valores de índice, contagens e similares: use NSInteger ou NSUInteger.

Não faz mal usar o NSInteger na maioria das circunstâncias, exceto pelo fato de ocupar o dobro de memória. O impacto na memória é muito pequeno, mas se você tiver uma quantidade enorme de números flutuando a qualquer momento, poderá fazer diferença usar ints.

Se você usar o NSInteger ou o NSUInteger, convém convertê-los em inteiros longos ou inteiros longos não assinados ao usar seqüências de caracteres de formato, pois o novo recurso Xcode retornará um aviso se você tentar efetuar logoff de um NSInteger como se tivesse um comprimento conhecido. Da mesma forma, você deve ter cuidado ao enviá-los para variáveis ​​ou argumentos digitados como ints, pois você pode perder alguma precisão no processo.

No geral, se você não espera ter centenas de milhares deles na memória de uma só vez, é mais fácil usar o NSInteger do que se preocupar constantemente com a diferença entre os dois.

Cinza
fonte
9

No momento (setembro de 2014), eu recomendaria o uso NSInteger/CGFloatao interagir com as APIs do iOS etc. se você também estiver criando seu aplicativo para arm64. Isso é porque você provavelmente irá obter resultados inesperados quando você usa os float, longe inttipos.

EXEMPLO: FLUTUAÇÃO / DUPLA vs CGFLOAT

Como exemplo, usamos o método delegado UITableView tableView:heightForRowAtIndexPath: .

Em um aplicativo somente de 32 bits, ele funcionará bem se for escrito assim:

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

floaté um valor de 32 bits e os 44 que você está retornando são um valor de 32 bits. No entanto, se compilarmos / executarmos o mesmo trecho de código em uma arquitetura arm64 de 64 bits, o 44 será um valor de 64 bits. Retornar um valor de 64 bits quando um valor de 32 bits é esperado fornecerá uma altura de linha inesperada.

Você pode resolver esse problema usando o CGFloattipo

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

Esse tipo representa 32 bits floatem um ambiente de 32 bits e um de 64 bitsdouble em um ambiente de 64 bits. Portanto, ao usar esse tipo, o método sempre receberá o tipo esperado, independentemente do ambiente de compilação / tempo de execução.

O mesmo vale para métodos que esperam números inteiros. Esses métodos esperam um intvalor de 32 bits em um ambiente de 32 bits e um de 64 bits longem um ambiente de 64 bits. Você pode resolver esse caso usando o tipo NSIntegerque serve como um intou com longbase no ambiente de compilação / tempo de execução.

Leon Lucardie
fonte
E se eu souber que o valor dessa variável em particular não pode conter valores numéricos grandes e desejar usar int, portanto. Funcionará bem em um ambiente de 64 bits. Eu acho que deveria também, já que eu não vi um loop for assim: for (int i = 0; i <10; i ++) fazendo qualquer comportamento errado, independentemente do ambiente em que ele é executado.
Chanchal Raj
@Chanchal Raj Desde que não haja conversão ou conversão para outros tipos ou uso / substituição de classes e métodos de terceiros que envolvam essa variável, usar um int em vez de NSInteger será bom.
Leon Lucardie
9

No iOS, atualmente não importa se você usa intou NSInteger. Será mais importante se / quando o iOS passar para 64 bits.

Simplificando, NSIntegers são ints no código de 32 bits (e, portanto, 32 bits) e longs no código de 64 bits ( longs no código de 64 bits têm largura de 64 bits, mas 32 bits no código de 32 bits). O motivo mais provável para usar em NSIntegervez de longé não quebrar o código de 32 bits existente (que usa ints).

CGFloattem o mesmo problema: em 32 bits (pelo menos no OS X), é float; em 64 bits, é double.

Atualização: com a introdução do iPhone 5s, iPad Air, iPad Mini com Retina e iOS 7, agora você pode criar código de 64 bits no iOS.

Atualização 2: Além disso, o uso de NSIntegers ajuda na interoperabilidade do código Swift.

MaddTheSane
fonte
0

int = 4 bytes (tamanho fixo independente do arquiteto) NSInteger = depende do tamanho do arquiteto (por exemplo, para arquiteto de 4 bytes = tamanho do NSInteger de 4 bytes)

Ankit garg
fonte