Por que uma variável NSInteger precisa ser convertida para longa quando usada como argumento de formato?

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

O código acima produz um erro:

Valores do tipo 'NSInteger' não devem ser usados ​​como argumentos de formato; adicione uma conversão explícita a 'long'

A NSLogmensagem corrigida é realmente NSLog(@"%lg", (long) myInt);. Por que preciso converter o valor inteiro de myIntpara longse eu quero que o valor seja exibido?

Daniel Lee
fonte
1
@DanielLee, se você usar NSLog(@"%ld", (long) myInt);, o longelenco é torná-lo corresponder-se com o lqualificador de %ld, mas tudo isso é desnecessário como NSLog(@"%d", myInt);é suficiente (uma vez que podemos ver que myIntnão é long. A linha inferior, você joga myIntse estiver usando longa qualificador em formato string, mas não há necessidade de usar um qualificador de longo formato de corda ou longfundido aqui.
Rob
1
Aparentemente, não é verdade que o NSLog (@ "% i", myInt); é suficiente porque você receberá a mensagem de erro como mostrei acima.
Daniel Lee
2
@DanielLee Ver o comentário de Martin R. Você postou sua pergunta com a tag iOS (onde nãoNSInteger é longa), mas parece que você está compilando com o destino do OS X (onde está ). NSInteger long
18713 Rob
Ahh, entendi. Eu não sabia que o iOS e o OSX tornariam o NSInteger diferente em bit e tipo.
Daniel Lee

Respostas:

193

Você recebe esse aviso se compilar no OS X (64 bits), porque nessa plataforma NSIntegeré definido como longe é um número inteiro de 64 bits. O %iformato, por outro lado, é para int, que é de 32 bits. Portanto, o formato e o parâmetro real não coincidem em tamanho.

Como NSIntegeré de 32 ou 64 bits, dependendo da plataforma, o compilador recomenda adicionar uma conversão longgeralmente.

Atualização: como o iOS 7 também suporta 64 bits agora, você pode receber o mesmo aviso ao compilar para iOS.

Martin R
fonte
1
Eu recebo esse erro no iOS 7. Como apenas o iPhone 5S mais recente é de 64 bits, se eu o definir, haverá algum problema em dispositivos mais antigos de 32 bits?
Pritesh Desai #
25
@ BartSimpson: Com um caso explícito para "long", como em NSLog(@"%ld", (long) myInt), ele funciona corretamente em 32 e 64 bits.
Martin R
@ MartinR se estamos lançando, por que não usar muito tempo em primeiro lugar?
William Entriken
3
@FullDecent: Claro que você pode trabalhar com longa aqui: long myInt = [myNumber longValue];. Porém, muitos métodos da (Core) Foundation usam NS (U) Inteiro como parâmetro ou valor de retorno, portanto o problema geral permanece. Também pode fazer sentido no seu aplicativo usar o NS (U) Inteiro para obter um intervalo maior disponível em dispositivos de 64 bits.
Martin R
39

Você não precisa converter para nada se os especificadores de formato corresponderem aos seus tipos de dados. Veja a resposta de Martin R para obter detalhes sobre como NSIntegeré definido em termos de tipos nativos.

Portanto, para o código que deve ser criado para ambientes de 64 bits, você pode escrever suas instruções de log desta maneira:

NSLog(@"%ld",  myInt); 

enquanto em ambientes de 32 bits, você pode escrever:

NSLog(@"%d",  myInt); 

e tudo funcionará sem elencos.

Uma razão para usar as conversões de qualquer maneira é que um bom código tende a ser portado entre plataformas, e se você converter suas variáveis ​​explicitamente, ele será compilado de maneira limpa em 32 e 64 bits:

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

E observe que isso é verdade não apenas para as instruções NSLog, que são apenas auxiliares de depuração, mas também para [NSString stringWithFormat:]e as várias mensagens derivadas, que são elementos legítimos do código de produção.

Monolo
fonte
1
Portanto, agora que esse hack é necessário, ainda é uma prática recomendada usar o NSInteger em primeiro lugar?
William Entriken
@ FullDecent É apenas um problema no código que é interpretado em tempo de execução, como seqüências de caracteres de formato. Todo o código compilado aproveita o NSInteger typedef.
Monolo
É uma prática recomendada usar o NSInteger, porque existem boas razões para que seja definido da maneira como é definido.
gnasher729
22

Em vez de passar um NSInteger para o NSLog, basta passar um NSNumber. Isso contornará todos os elencos e a escolha do especificador de formato de sequência correto.

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

Também funciona para o NSUIntegers sem precisar se preocupar com isso. Veja a resposta para o NSInteger e o NSUInteger em um ambiente misto de 64 bits / 32 bits

orkoden
fonte
2
Suponho que a resposta selecionada seja tecnicamente a melhor resposta para a pergunta, mas se você quiser saber como evitar a transmissão de cada ocorrência e evitar avisos, acho que essa é a melhor solução.
Daniel Madeira
0

Ele mantém o aviso durante o uso NSLog(@"%ld", (long)myInt);, mas para de avisar após a declaração de alteração long myInt = 1804809223;no iOS 10.

Yao Li
fonte
-2

O OS X usa vários tipos de dados - NSInteger, NSUInteger, CGFloat e CFIndex - para fornecer um meio consistente de representação de valores em ambientes de 32 e 64 bits. Em um ambiente de 32 bits, NSInteger e NSUInteger são definidos como int e unsigned int, respectivamente. Em ambientes de 64 bits, o NSInteger e o NSUInteger são definidos como longos e sem assinatura, respectivamente. Para evitar a necessidade de usar diferentes especificadores do tipo printf, dependendo da plataforma, você pode usar os especificadores mostrados neste link para ambientes de 32 e 64 bits.

Saheb Singh
fonte