Cómo incrementar un NSNumber

108

¿Cómo incremento un NSNumber?

es decir, myNSNumber ++

TheLearner
fuente
2
Recuerde: cuando utilice NSNumber *, compruebe siempre si los tipos primitivos son suficientes.
WKS
1
Me sorprende que no haya una respuesta que proporcione una forma rápida / optimizada de hacer eso, porque solo para aumentar el valor necesita consultar, combinar y crear un nuevo objeto, por lo que hacer esto en bucle puede llevar mucho tiempo.
PetrV

Respuestas:

123

Actualización: FYI, personalmente me gustan más las respuestas de una línea de BoltClock y DarkDusts. Son más concisos y no requieren variables adicionales.


Para incrementar un NSNumber, tendrá que obtener su valor, incrementarlo y almacenarlo en una nuevaNSNumber .

Por ejemplo, para NSNumbermantener un número entero:

NSNumber *number = [NSNumber numberWithInt:...];
int value = [number intValue];
number = [NSNumber numberWithInt:value + 1];

O para NSNumbermantener un número de punto flotante:

NSNumber *number = [NSNumber numberWithDouble:...];
double value = [number doubleValue];
number = [NSNumber numberWithDouble:value + 1.0];
Itai Ferber
fuente
62
Esta es la razón número uno por la que odio las propiedades NSNumber en Core Data. NSNumber es muy incómodo para trabajar y realizar matemáticas.
Christian Schlensker
de nuevo, NSNumber no funcionó mientras se inicializaba con cero para dar valor de basura.
Jignesh B
lo siento, ¿qué significa el ...soporte? Esa es una referencia de pseudocódigo, ¿verdad? XCode no lo hace...
boulder_ruby
@boulder_ruby El ...es solo un marcador de posición para cualquier número que desee.
Itai Ferber
126

NSNumberlos objetos son inmutables; lo mejor que puede hacer es tomar el valor primitivo, incrementarlo y luego envolver el resultado en su propio NSNumberobjeto:

NSNumber *bNumber = [NSNumber numberWithInt:[aNumber intValue] + 1];
BoltClock
fuente
28
Idiomas modernos: 1, Objective-C: 0.
devios1
77

Para cualquiera que esté usando la última versión de Xcode (escribiendo esto a partir de 4.4.1, SDK 5.1), con el uso de objetos literales, puede limpiar el código incluso un poco más ...

NSNumber *x = @(1);
x = @([x intValue] + 1);
// x = 2

Sigue siendo un poco doloroso lidiar con el boxeo y desempaquetar todo para hacer operaciones simples, pero está mejorando, o al menos más corto.

shortstuffsushi
fuente
6
@softRli ¡Visite clang.llvm.org/docs/ObjectiveCLiterals.html para ver más ObjC Literals! Muy útil en mi opinión.
chown el
1
Y para el caso, podría usar x - @ (x.intValue + 1), aunque a muchas personas no les gusta usar la notación de puntos para invocar métodos que no son de propiedad. Es más corto, pero podría llamarse abuso de la notación de puntos.
Duncan C
@boulder_ruby, es reasignación. NSNumbers son inmutables, por lo que para "incrementar" el valor, debe reasignarlo a un nuevo valor.
shortstuffsushi
30

NSNumbers son inmutables, debe crear una nueva instancia.

// Work with 64 bit to support very large values
myNSNumber = [NSNumber numberWithLongLong:[myNSNumber longLongValue] + 1];

// EDIT: With modern syntax:
myNSNumber = @([myNSNumber longLongValue] + 1);
DarkDust
fuente
24

Ponlos todos juntos y tienes:

myNSNumber = @(myNSNumber.intValue + 1);
JLundell
fuente
¿No está reasignando el valor de myNSNumber aquí? Pensé que eso no estaba permitido porque los NSNumberobjetos son inmutables (?)
boulder_ruby
1
Está creando un nuevo objeto NSNumber. myNSNumber es un puntero (NSNumber *) y tiene un nuevo valor cuando haya terminado.
JLundell
3

Me gusta la expresión de puntos porque es más concisa y es por eso que IOS la admite en primer lugar:

myNSNumber = [NSNumber numberWithInt:myNSNumber.intValue + 1];
Jack
fuente
la notación de puntos no está haciendo lo que usted piensa, y puede ser más lento a veces (podría ser predeterminado en "valueForKey:" si no existe un descriptor de acceso sintetizado para la "propiedad" (en su caso, intValue). intValue nunca se definió como @property de NSNumber.
Motti Shneor
2

Si lo está utilizando para almacenar un valor integral:

myNSNumber = @(myNSNumber.longLongValue + 1);

Para cosas de punto flotante, la respuesta corta es la siguiente, pero es una mala idea hacer esto, perderá precisión y si está haciendo algún tipo de comparación más adelante como [myNSNumber isEqual: @ (4.5)] puede que se sorprenda :

myNSNumber = @(myNSNumber.floatValue + 1);

Si necesita hacer matemáticas con números de punto flotante representados como objetos en Objective-C (es decir, si necesita ponerlos en matrices, diccionarios, etc.), debe usar NSDecimalNumber.

lms
fuente
Si va a votar en contra, también podría ser constructivo y decir por qué.
lms
Su respuesta implica que es NSNumber el que presenta problemas de redondeo. Eso no es correcto, NSNumber tiene el mismo comportamiento de redondeo que los tipos escalares. Además, su consejo de utilizar NSDecimalNumbers es bastante irritante. Ahí implica que son necesarios si necesita primero objetos ciudadanos en el objetivo-c. Pero, por supuesto, también hay números en los objetos. NSDecimalNumbers tienen una mayor precisión, pero también pueden producirse errores de redondeo. Y: op pregunta sobre el incremento, su respuesta con float y aritmética doble. Pero esos no tienen incremento, ya que no tienen un sucesor natural.
vikingosegundo
@vikingosegundo Está convirtiendo un NSNumber en un float que causa la pérdida de precisión porque NSNumber almacena números de forma abstracta donde float son base2, por lo que no es culpa del NSNumber, sino de la conversión de ida y vuelta. Y NSDecimalNumbers no provocará errores de redondeo para aritmética en base 10, y están diseñados para aritmética, lea el documento. El OP preguntó acerca de incrementar un NSNumber, y le di una respuesta correcta y legible para el caso integral, y también di una respuesta para el caso de punto flotante, pero lo desaconsejé y luego seguí con una sugerencia mejor.
lms
Una instancia puede representar cualquier número que se pueda expresar como exponente de mantisa x 10, donde mantisa es un número entero decimal de hasta 38 dígitos y el exponente es un número entero entre -128 y 127. No todos los números pueden expresarse así. También OP pidió un incremento sencillo. NSDecimalNumber no proporcionó eso. y aún así, su respuesta implica que necesita NSDecimalNumbers en el espacio de objetos. eso está mal.
vikingosegundo
Amigo, estaba tratando de ser realmente útil con mi respuesta, y pensé que el voto negativo se debía a que había algo mal y pensé que podría aprender algo nuevo. Ahora puedo ver que ese no es el caso. Sus comentarios no tienen sentido y le sugiero que lea sobre los sistemas numéricos porque un sistema numérico de base 10. (Re. Su cotización) no causará ningún error de redondeo para números base 10, no dije nada sobre precisión, solo precisión, pero 128 decimal también tiene mucha precisión.
lms
0

Utilice una categoría para facilitar el uso futuro. He aquí una idea básica.

 - (void)incrementIntBy:(int)ammount {
      self = [NSNumber numberWithInt:(self.intValue + ammount)];
 }
nizx
fuente
Ugh. ¿Una categoría que implementa un método de instancia que reemplaza a self?!? Eso es realmente desagradable. ¿Por qué no escribir un método de instancia que tenga un retorno no nulo del NSNumber resultante?
Duncan C
NSNumber se define como inmutable, por lo tanto, este método de categoría está violando su supuesta inmutabilidad (incluso si reemplaza a self). En cambio, una categoría mejor diseñada agregaría un nuevo inicializador a NSNumber, en la forma: + numberByIncrementingAmounf: (int) amount;
Motti Shneor
0

Mucha buena información en las respuestas a esta pregunta. Usé la respuesta seleccionada en mi código y funcionó. Luego comencé a leer el resto de las publicaciones y simplifiqué mucho el código. Es un poco más difícil de leer si no está familiarizado con los cambios en la notación que se introdujeron en Xcode 5, pero es mucho más limpio. Probablemente podría hacer solo una línea, pero luego es un poco difícil averiguar qué estoy haciendo.

Estoy almacenando un NSNumber en un diccionario y quiero incrementarlo. Lo consigo, lo convierto en un int, lo incremento mientras lo convierto en un NSNumber, luego lo vuelvo a poner en el diccionario.

Código antiguo

 NSNumber *correct = [scoringDictionary objectForKey:@"correct"];
 int correctValue = [correct intValue];
 correct = [NSNumber numberWithInt:correctValue + 1];
 [scoringDictionary removeObjectForKey:@"correct"];
 scoringDictionary[@"correct"] = correct;

Nuevo código

NSNumber *correct = [scoringDictionary objectForKey:@"correct"];
scoringDictionary[@"correct"] = @(correct.intValue + 1);
JScarry
fuente