objective ios objective-c ios8 nsuserdefaults

ios - nsuserdefaults objective c



NSUserDefaults no fiable en iOS 8 (12)

Tengo una aplicación que usa [NSUserDefaults standardUserDefaults] para almacenar información de la sesión. En general, esta información se verifica en el lanzamiento de la aplicación y se actualiza al salir de la aplicación. Descubrí que parece funcionar de manera no confiable en iOS 8.

Actualmente estoy probando en un iPad 2, aunque puedo probar en otros dispositivos si es necesario.

Algunas veces, los datos escritos antes de la salida no persisten en el inicio de la aplicación. Igualmente, las claves eliminadas antes de la salida a veces parecen existir después del lanzamiento.

He escrito el siguiente ejemplo para intentar ilustrar el problema:

- (void)viewDidLoad { [super viewDidLoad]; NSData *_dataArchive = [[NSUserDefaults standardUserDefaults] objectForKey:@"Session"]; NSLog(@"Value at launch - %@", _dataArchive); NSString *testString = @"TESTSTRING"; [[NSUserDefaults standardUserDefaults] setObject:testString forKey:@"Session"]; [[NSUserDefaults standardUserDefaults] synchronize]; _dataArchive = [[NSUserDefaults standardUserDefaults] objectForKey:@"Session"]; NSLog(@"Value after adding data - %@", _dataArchive); [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Session"]; [[NSUserDefaults standardUserDefaults] synchronize]; _dataArchive = [[NSUserDefaults standardUserDefaults] objectForKey:@"Session"]; NSLog(@"Value before exit - %@", _dataArchive); exit(0); }

Ejecutando el código anterior, yo (generalmente) obtengo el resultado a continuación (que es lo que esperaría):

Value at launch - (null) Value after adding data - TESTSTRING Value after deleting data - (null)

Si luego comento las líneas que eliminan la clave:

//[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Session"]; //[[NSUserDefaults standardUserDefaults] synchronize];

Y ejecutar la aplicación tres veces, esperaría ver:

Value at launch - (null) Value after adding data - TESTSTRING Value after deleting data - TESTSTRING Value at launch - TESTSTRING Value after adding data - TESTSTRING Value before exit - TESTSTRING Value at launch - TESTSTRING Value after adding data - TESTSTRING Value before exit - TESTSTRING

Pero la salida que realmente veo es:

Value at launch - (null) Value after adding data - TESTSTRING Value after deleting data - TESTSTRING Value at launch - (null) Value after adding data - TESTSTRING Value after deleting data - TESTSTRING Value at launch - (null) Value after adding data - TESTSTRING Value after deleting data - TESTSTRING

por ejemplo, parece que no está actualizando el valor al salir de la aplicación.

EDITAR : He probado el mismo código en un iPad 2 con iOS 7.1.2; y parece funcionar correctamente todo el tiempo.

TLDR: en iOS 8, ¿[NSUserDefaults standardUserDefaults] funciona de forma no confiable? Y si es así, ¿hay una solución / solución?


Como dijo gnasher729, no llame a exit (). Puede haber un problema con NSUserDefaults en iOS8, pero llamar a exit () simplemente no funcionará.

Debería ver los comentarios de David Smith en NSUserDefaults ( https://gist.github.com/anonymous/8950927 ):

Terminar una aplicación anormalmente (presión de memoria, muerte, bloqueo, parada en Xcode) es como reiniciar el git --hard HEAD, y dejando


Como esta es una aplicación empresarial y no una aplicación de la tienda de aplicaciones, puede intentar:

@interface UIApplication() -(void) _terminateWithStatus:(int)status; @end

y luego llama:

[UIApplication.sharedApplication _terminateWithStatus:0];

Está utilizando una API no documentada, por lo que puede no funcionar en versiones anteriores o futuras de iOS.


Como otros señalaron usar exit () y salir de tu aplicación, es una mala idea en iOS.

Pero probablemente sé con qué tienes que lidiar. Desarrollamos también una aplicación empresarial y aunque tratamos de convencer al cliente de que en iOS está en contra de todas las reglas y mejores prácticas, insistieron en cerrar la aplicación en un punto.

En lugar de exit () usamos este fragmento de código:

UIApplication *app = [UIApplication sharedApplication]; [app performSelector:@selector(suspend)];

Como su nombre indica, solo suspende la aplicación como si el usuario presionara el botón de inicio. Por lo tanto, sus métodos de guardado podrían finalizar correctamente.

Sin embargo, no he probado esta solución para su caso particular, y no estoy seguro de si la suspensión es suficiente para usted, pero para nosotros funcionó.


Encontré NSUserDefaults para comportarse bien en iOS 8.4 cuando uso un nombre de suite para crear una instancia en lugar de confiar en standardUserDefaults .

NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"MySuiteName"];


Enfrenté el mismo problema. Lo resolví llamando

[[NSUserDefaults standardUserDefaults] synchronize];

antes de llamar

[[NSUserDefaults standardUserDefaults] stringForKey:@"my_key"] .

Resulta que hay que llamar a synchronize no solo después de la configuración, sino también antes de hacerlo.


Es un error en los simuladores. Esta falla también existe antes de iOS8 beta4 en los dispositivos. Pero en los dispositivos este error está resuelto pero actualmente existe en los simuladores. También han cambiado la estructura de los directorios del simulador. Si reinicias tu simulador funcionará bien. .En dispositivos iOS8 también funcionará bien.


He resuelto problemas similares haciendo cambios en NSUserDefaults solo en el hilo principal.


Llamar a exit () en una aplicación iOS es una ofensa criminal, y como habrás notado, es castigado. Nunca abandonas una aplicación iOS tú mismo. Nunca.


Lo encontré en Foundation Framework Reference, creo que será útil:

La clase NSUserDefaults proporciona métodos prácticos para acceder a tipos comunes como flotantes, dobles, enteros, booleanos y URL. Un objeto predeterminado debe ser una lista de propiedades, es decir, una instancia de (o para colecciones una combinación de instancias de): NSData, NSString, NSNumber, NSDate, NSArray o NSDictionary. Si desea almacenar cualquier otro tipo de objeto, normalmente debe archivarlo para crear una instancia de NSData. Para obtener más detalles, consulte la Guía de programación de preferencias y configuraciones.


Parece que a iOS 8 no le gusta establecer cadenas en NSUserDefaults. Intente codificar la cadena en NSData antes de guardar.

Al guardar:

[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:testString] forKey:@"Session"];

Al leer:

NSData *_data = [[NSUserDefaults standardUserDefaults] objectForKey:@"Session"]; NSString *_dataArchive = [NSKeyedUnarchiver unarchiveObjectWithData:_data];

Espero que esto ayude.


Tengo el mismo problema con iOS 8 y la única solución que funcionó para mí es retrasar el rendimiento de la función exit () alguna duración (Ej .: 0.1 segundos) usando:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), dispatch_get_main_queue(), ^{ exit(0); });

o crea un método y luego llámalo usando performSelector: withObject: afterDelay:

- (void)exitApp { exit(0); } [self performSelector:@selector(exitApp) withObject:nil afterDelay:0.1];


iOS 8 introdujo una serie de cambios de comportamiento en NSUserDefaults . Si bien la API NSUserDefaults ha cambiado poco, el comportamiento ha cambiado en formas que pueden ser relevantes para su aplicación. Por ejemplo, se desaconseja usar -synchronize (y siempre lo ha sido). Los cambios adicionales a otras partes de Foundation y CoreFoundation, como la coordinación de archivos y los cambios relacionados con los contenedores compartidos, pueden afectar su aplicación y su uso de NSUserDefaults .

Escribir en NSUserDefaults en particular ha cambiado debido a esto. La escritura lleva más tiempo y puede haber otros procesos que compitan por el acceso al almacenamiento predeterminado de la aplicación. Si está intentando escribir en NSUserDefaults cuando su aplicación está saliendo, su aplicación puede finalizar antes de que la escritura se confirme bajo algunos escenarios. Terminar con fuerza usando la exit(0) en su ejemplo es muy probable que estimule este comportamiento. Normalmente, cuando se cierra una aplicación, el sistema puede realizar tareas de limpieza y esperar a que finalicen las operaciones pendientes de archivos: cuando finaliza la aplicación con exit() o con el depurador, es posible que esto no suceda.

En general, NSUserDefaults es confiable cuando se usa correctamente en iOS 8.

Estos cambios se describen en las notas de la versión de la Fundación para OS X 10.10 (actualmente no hay una nota de lanzamiento de Foundation por separado para iOS 8).