¿Por qué una variable NSInteger tiene que convertirse en larga cuando se usa como argumento de formato?

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

El código anterior produce un error:

Los valores de tipo 'NSInteger' no deben usarse como argumentos de formato; agregue un reparto explícito a 'largo' en su lugar

El NSLogmensaje corregido es en realidad NSLog(@"%lg", (long) myInt);. ¿Por qué tengo que convertir el valor entero de myInta longsi quiero que se muestre el valor?

Daniel Lee
fuente
1
@DanielLee, si se utiliza NSLog(@"%ld", (long) myInt);, el longreparto es para que coincida con el lcalificador de %ld, pero todo eso es innecesario, ya que NSLog(@"%d", myInt);es suficiente (dado que podemos ver que myIntno es long. En pocas palabras, se echó myIntsi se utiliza mucho en el formato de clasificación cadena, pero no es necesario usar el calificador de formato de cadena larga o longemitir aquí.
Rob
1
Aparentemente, no es cierto que NSLog (@ "% i", myInt); es suficiente porque recibirás el mensaje de error que he mostrado anteriormente.
Daniel Lee
2
@DanielLee Vea el comentario de Martin R. Publicó su pregunta con la etiqueta de iOS (donde noNSInteger es larga), pero parece que está compilando con el objetivo de OS X (donde está ). NSInteger long
Rob
Ahh ya veo. No sabía que iOS y OSX harían que NSInteger fuera diferente en bits y tipo.
Daniel Lee

Respuestas:

193

Recibirá esta advertencia si compila en OS X (64 bits), porque en esa plataforma NSIntegerse define como longy es un entero de 64 bits. El %iformato, por otro lado, es paraint , que es de 32 bits. Por lo tanto, el formato y el parámetro real no coinciden en tamaño.

Dado que NSIntegeres de 32 bits o de 64 bits, dependiendo de la plataforma, el compilador recomienda agregar un reparto en longgeneral.

Actualización: dado que iOS 7 ahora también es compatible con 64 bits, puede obtener la misma advertencia al compilar para iOS.

Martin R
fuente
1
Recibo este error en iOS 7. Dado que solo el último iPhone 5S es de 64 bits si lo configuro, ¿causará algún problema en los dispositivos de 32 bits más antiguos?
Pritesh Desai
25
@BartSimpson: con un caso explícito a "largo", como en NSLog(@"%ld", (long) myInt), funciona correctamente en 32 bits y 64 bits.
Martin R
@MartinR si estamos lanzando, ¿por qué no usar mucho tiempo en primer lugar?
William Entriken
3
@FullDecent: Por supuesto que puede trabajar con mucho aquí: long myInt = [myNumber longValue];. Pero muchos métodos Foundation (Core) usan NS (U) Integer como parámetro o valor de retorno, por lo que el problema general persiste. También puede tener sentido en su aplicación usar NS (U) Integer para obtener un mayor rango disponible en dispositivos de 64 bits.
Martin R
39

No tiene que emitir nada si sus especificadores de formato coinciden con sus tipos de datos. Consulte la respuesta de Martin R para obtener detalles sobre cómo NSIntegerse define en términos de tipos nativos.

Entonces, para el código destinado a ser construido para entornos de 64 bits, puede escribir sus declaraciones de registro de esta manera:

NSLog(@"%ld",  myInt); 

mientras que para entornos de 32 bits puede escribir:

NSLog(@"%d",  myInt); 

y todo funcionará sin yeso.

Una razón para usar conversiones de todos modos es que un buen código tiende a ser portado a través de plataformas, y si convierte sus variables explícitamente, se compilará limpiamente en 32 y 64 bits:

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

Y tenga en cuenta que esto es cierto no solo para las declaraciones NSLog, que son solo ayudas para la depuración, sino también para [NSString stringWithFormat:]los diversos mensajes derivados, que son elementos legítimos del código de producción.

Monolo
fuente
1
Entonces, ahora que se requiere este truco, ¿sigue siendo la mejor práctica usar NSInteger en primer lugar?
William Entriken
@FullDecent Solo es un problema en el código que se interpreta en tiempo de ejecución, como las cadenas de formato. Al código compilado aprovecha el tipo de definición NSInteger.
Monolo
Es una buena práctica usar NSInteger, porque hay buenas razones por las que se define de la forma en que se define.
gnasher729
22

En lugar de pasar un NSInteger a NSLog, simplemente pase un NSNumber. Esto evitará todos los modelos y elegirá el especificador de formato de cadena correcto.

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

También funciona para NSUIntegers sin tener que preocuparse por eso. Vea la respuesta a NSInteger y NSUInteger en un entorno mixto de 64 bits / 32 bits

orkoden
fuente
2
Supongo que la respuesta seleccionada es técnicamente la mejor respuesta a la pregunta, pero si desea saber cómo evitar emitir cada aparición y evitar advertencias, creo que esta es la mejor solución.
Daniel Wood
0

Mantiene la advertencia durante el uso NSLog(@"%ld", (long)myInt);, pero detiene la advertencia después de la declaración de cambio long myInt = 1804809223;en iOS 10.

Yao Li
fuente
-2

OS X utiliza varios tipos de datos (NSInteger, NSUInteger, CGFloat y CFIndex) para proporcionar un medio consistente de representar valores en entornos de 32 y 64 bits. En un entorno de 32 bits, NSInteger y NSUInteger se definen como int y unsigned int, respectivamente. En entornos de 64 bits, NSInteger y NSUInteger se definen como largos y sin signo, respectivamente. Para evitar la necesidad de usar diferentes especificadores de tipo estilo printf según la plataforma, puede usar los especificadores que se muestran en este enlace para entornos de 32 bits y 64 bits.

Saheb Singh
fuente