Estoy programando una aplicación para iPhone y necesito forzarla a salir debido a ciertas acciones del usuario. Después de limpiar la memoria asignada por la aplicación, ¿cuál es el método apropiado para llamar para finalizar la aplicación?
Estoy programando una aplicación para iPhone y necesito forzarla a salir debido a ciertas acciones del usuario. Después de limpiar la memoria asignada por la aplicación, ¿cuál es el método apropiado para llamar para finalizar la aplicación?
Respuestas:
¿Lo has intentado exit(0)
?
Alternativamente, [[NSThread mainThread] exit]
aunque no lo he intentado, parece ser la solución más adecuada.
En el iPhone no existe el concepto de salir de una aplicación. La única acción que debería hacer que una aplicación se cierre es tocar el botón Inicio en el teléfono, y eso no es algo a lo que los desarrolladores tengan acceso.
Según Apple, su aplicación no debe finalizar por sí sola. Dado que el usuario no presionó el botón de Inicio, cualquier regreso a la pantalla de Inicio le da la impresión de que su aplicación se bloqueó. Este es un comportamiento confuso, no estándar y debe evitarse.
La salida (0) aparece ante un usuario como bloqueos, por lo que debe mostrar un mensaje de confirmación al usuario Después de la confirmación, suspenda (presione el botón de inicio mediante programación) y espere 2 segundos mientras la aplicación pasa a segundo plano con animación y luego salga detrás de la vista del usuario
-(IBAction)doExit
{
//show confirmation message to user
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Confirmation"
message:@"Do you want to exit?"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK", nil];
[alert show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0) // 0 == the cancel button
{
//home button press programmatically
UIApplication *app = [UIApplication sharedApplication];
[app performSelector:@selector(suspend)];
//wait 2 seconds while app is going background
[NSThread sleepForTimeInterval:2.0];
//exit app when app is in background
exit(0);
}
}
exit(0)
no importa. El punto es que su aplicación tiene "comportamiento de abandono". El comportamiento de abandono en sí está prohibido en AppStore, excepto en algunas aplicaciones creadas por terceros muy importantes. Además, imitar el comportamiento del botón de inicio también está sujeto a ser rechazado.
Consulte las preguntas y respuestas aquí: https://developer.apple.com/library/content/qa/qa1561/_index.html
P: ¿Cómo salgo programáticamente de mi aplicación iOS?
No se proporciona una API para finalizar con gracia una aplicación de iOS.
En iOS, el usuario presiona el botón de Inicio para cerrar las aplicaciones. Si su aplicación tiene condiciones en las que no puede proporcionar la función prevista, el enfoque recomendado es mostrar una alerta para el usuario que indique la naturaleza del problema y las posibles acciones que el usuario podría tomar: encender WiFi, habilitar los servicios de ubicación, etc. Permita que el usuario finalice la aplicación a su propia discreción.
ADVERTENCIA: no llame a la
exit
función. Las aplicaciones que llamanexit
parecerán que el usuario se ha bloqueado, en lugar de realizar una finalización elegante y volver a la pantalla de inicio.Además, es posible que los datos no se guarden porque no se invocarán métodos
-applicationWillTerminate:
similaresUIApplicationDelegate
si llama a exit.Si durante el desarrollo o las pruebas es necesario finalizar su aplicación, se recomienda la
abort
función oassert
macro
No es realmente una forma de abandonar el programa, sino una forma de obligar a las personas a renunciar.
UIAlertView *anAlert = [[UIAlertView alloc] initWithTitle:@"Hit Home Button to Exit" message:@"Tell em why they're quiting" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[anAlert show];
Vaya a su info.plist y verifique la clave "La aplicación no se ejecuta en segundo plano". Esta vez, cuando el usuario hace clic en el botón de inicio, la aplicación se cierra por completo.
Agregar UIApplicationExitsOnSuspend
propiedad application-info.plist
a true
.
Después de algunas pruebas, puedo decir lo siguiente:
[UIApplication sharedApplication]
hará que la aplicación parezca bloqueada, PERO llamará - (void)applicationWillTerminate:(UIApplication *)application
antes de hacerlo;exit(0);
también terminará la aplicación, pero se verá "normal" (los iconos del trampolín aparecen como se esperaba, con el efecto de alejamiento), PERO no llamará al - (void)applicationWillTerminate:(UIApplication *)application
método de delegado.Mi consejo:
- (void)applicationWillTerminate:(UIApplication *)application
al delegado.exit(0);
.Su ApplicationDelegate recibe una notificación de abandono intencional por parte del usuario:
- (void)applicationWillResignActive:(UIApplication *)application {
Cuando recibo esta notificación solo llamo
exit(0);
Que hace todo el trabajo. Y lo mejor es que es la intención del usuario dejar de fumar, por eso no debería ser un problema llamarlo allí.
En mi aplicación de audio, era necesario cerrar la aplicación después de que las personas sincronizaran su dispositivo mientras la música todavía estaba sonando. Tan pronto como se completa la sincronización, recibo una notificación. Pero salir de la aplicación justo después de eso en realidad se vería como un bloqueo.
Entonces, en su lugar, configuré una bandera para REALMENTE salir de la aplicación en la siguiente acción de fondo. Lo cual está bien para actualizar la aplicación después de una sincronización.
Mi aplicación ha sido rechazada recientemente porque he usado un método no documentado. Literalmente:
"Desafortunadamente no se puede agregar a la tienda de aplicaciones porque está usando una API privada. El uso de API no públicas, que como se describe en la sección 3.3.1 del Acuerdo de licencia del programa para desarrolladores de iPhone está prohibido:
"3.3.1 Las aplicaciones solo pueden usar API documentadas de la manera prescrita por Apple y no deben usar ni llamar a ninguna API privada".
La API no pública que se incluye en su aplicación es terminateWithSuccess "
Apple dice:
"Advertencia: no llame a la función de salida. Las aplicaciones que llaman a la salida parecerán que el usuario se ha bloqueado, en lugar de realizar una finalización elegante y animar a la pantalla de inicio".
Creo que esta es una mala suposición. Si el usuario toca un botón para salir y aparece un mensaje que dice algo como: "La aplicación ahora se cerrará", no parece que se haya bloqueado. Apple debería proporcionar una forma válida de salir de una aplicación (no salir (0)).
Esto obtuvo una buena respuesta pero decidió expandirse un poco:
No puede hacer que su aplicación sea aceptada en AppStore sin leer bien las Directrices de interfaz humana iOS de Apple. (se reservan el derecho de rechazarlo por hacer algo en contra de ellos) La sección "No salga programáticamente" http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices. HTML es una guía exacta sobre cómo debe tratar en este caso.
Si alguna vez tiene un problema con la plataforma Apple para la que no puede encontrar fácilmente una solución, consulte a HIG. Es posible que Apple simplemente no quiera que lo hagas y por lo general (no soy Apple, así que no puedo garantizarlo siempre) lo dicen en su documentación.
Hm, es posible que "tenga que" cerrar la aplicación si, por ejemplo, su aplicación requiere una conexión a Internet. Puede mostrar una alerta y luego hacer algo como esto:
if ([[UIApplication sharedApplication] respondsToSelector:@selector(terminate)]) {
[[UIApplication sharedApplication] performSelector:@selector(terminate)];
} else {
kill(getpid(), SIGINT);
}
No podemos dejar de usar la aplicación exit(0)
, abort()
funciones, ya que Apple desaconseja el uso de estas funciones. Aunque puede utilizar estas funciones para fines de desarrollo o prueba.
Si durante el desarrollo o las pruebas es necesario finalizar su aplicación, se recomienda la función de cancelación o la macro de afirmación
Encuentre este hilo de preguntas y respuestas de Apple para obtener más información.
A medida que el uso de esta función crea impresión, la aplicación se está bloqueando Así que recibí algunas sugerencias, como que podemos mostrar Alerta con mensaje de finalización al usuario consciente sobre el cierre de la aplicación, debido a la falta de disponibilidad de ciertas funciones.
Pero la directriz de interfaz humana de iOS para iniciar y detener la aplicación sugiere que nunca use el botón Salir o Cerrar para finalizar la aplicación. Más bien que están sugiriendo mostrar un mensaje apropiado para explicar la situación.
Una aplicación iOS nunca muestra una opción Cerrar o Salir. Las personas dejan de usar una aplicación cuando cambian a otra aplicación, regresan a la pantalla de inicio o ponen sus dispositivos en modo de suspensión.
Nunca salga de una aplicación de iOS mediante programación. La gente tiende a interpretar esto como un accidente. Si algo impide que su aplicación funcione según lo previsto, debe informar a los usuarios sobre la situación y explicar qué pueden hacer al respecto.
Además de lo anterior, buena respuesta que solo quería agregar, piensa en limpiar tu memoria.
Después de que su aplicación se cierre, el sistema operativo iPhone limpiará automáticamente todo lo que su aplicación dejó atrás, por lo que liberar toda la memoria manualmente puede aumentar la cantidad de tiempo que le toma a su aplicación salir.
- (IBAction)logOutButton:(id)sender
{
//show confirmation message to user
CustomAlert* alert = [[CustomAlert alloc] initWithTitle:@"Confirmation" message:@"Do you want to exit?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
alert.style = AlertStyleWhite;
[alert setFontName:@"Helvetica" fontColor:[UIColor blackColor] fontShadowColor:[UIColor clearColor]];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0) // 0 == the cancel button
{
//home button press programmatically
UIApplication *app = [UIApplication sharedApplication];
[app performSelector:@selector(suspend)];
//wait 2 seconds while app is going background
[NSThread sleepForTimeInterval:2.0];
//exit app when app is in background
NSLog(@"exit(0)");
exit(0);
}
}
Utilicé el enfoque [[NSMutableArray new] addObject: nil] mencionado anteriormente para forzar el cierre (bloqueo) de la aplicación sin hacer una llamada de función de salida (0).
¿Por qué? Porque mi aplicación utiliza la fijación de certificados en todas las llamadas de API de red para evitar ataques de intermediario. Estos incluyen las llamadas de inicialización que hace mi aplicación financiera al inicio.
Si la autenticación del certificado falla, todas mis llamadas de inicialización tienen un error y dejan mi aplicación en un estado indeterminado. Dejar que el usuario vaya a casa y luego volver a la aplicación no ayuda, ya que a menos que la aplicación haya sido purgada por el sistema operativo, todavía no se ha inicializado y no es confiable.
Entonces, en este caso, consideramos que es mejor hacer estallar una alerta informando al usuario que la aplicación está operando en un entorno inseguro y luego, cuando presionan "Cerrar", forzar el cierre de la aplicación usando el método mencionado anteriormente.
[[UIApplication sharedApplication] terminateWithSuccess];
Funcionó bien y llama automáticamente
- (void)applicationWillTerminateUIApplication *)application delegate.
para eliminar la advertencia de tiempo de compilación, agregue este código
@interface UIApplication(MyExtras)
- (void)terminateWithSuccess;
@end
No debe llamar directamente a la función, exit(0)
ya que cerrará la aplicación inmediatamente y parecerá que su aplicación está bloqueada. Por lo tanto, es mejor mostrar a los usuarios una alerta de confirmación y dejar que lo hagan ellos mismos.
func askForQuit(_ completion:@escaping (_ canQuit: Bool) -> Void) {
let alert = UIAlertController(title: "Confirmation!", message: "Do you want to quit the application", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
completion(true)
}))
alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
completion(false)
}))
self.present(alert, animated: true, completion: nil)
}
/// Will quit the application with animation
func quit() {
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
/// Sleep for a while to let the app goes in background
sleep(2)
exit(0)
}
self.askForQuit { (canQuit) in
if canQuit {
self.quit()
}
}
El usuario debe decidir cuándo sale una aplicación. No creo que sea una buena interacción del usuario cuando se cierra una aplicación. Por lo tanto, no hay una buena API para ello, solo el botón de inicio tiene uno.
Si hay un error: impleméntelo mejor o notifique al usuario. Si tiene que reiniciarse: impleméntelo mejor de Notificar al usuario.
Suena tonto, pero es una mala práctica salir de la aplicación sin dejar que el usuario decida y no notificarlo. Y dado que hay un botón de inicio para la interacción del usuario, Apple afirma que no debería haber 2 cosas para la misma función (salir de una aplicación).
Salir de una aplicación de otra manera que no sea el botón de inicio es un enfoque realmente no iOS-esque .
Sin embargo, hice este ayudante que no usa cosas privadas:
void crash()
{ [[NSMutableArray new] addObject:NSStringFromClass(nil)]; }
Pero todavía no está destinado a la producción en mi caso. Es para probar informes de fallas o para reiniciar rápidamente después de un reinicio de Core Data. Solo aseguré que no se rechazara si la función quedaba en el código de producción.
En iPadOS 13 ahora puede cerrar todas las sesiones de escena como esta:
for session in UIApplication.shared.openSessions {
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
}
Esto llamará applicationWillTerminate(_ application: UIApplication)
a su delegado de aplicación y terminará la aplicación al final.
Pero cuidado con dos cosas:
Ciertamente, esto no debe usarse para cerrar todas las escenas. (ver https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/multiple-windows/ )
Se compila y funciona bien en iOS 13 en un iPhone, pero parece no hacer nada.
Más información sobre escenas en iOS / iPadOS 13: https://developer.apple.com/documentation/uikit/app_and_environment/scenes
Puede ser apropiado salir de una aplicación si es una aplicación de larga duración que también se ejecuta en segundo plano, por ejemplo, para obtener actualizaciones de ubicación (utilizando la capacidad de fondo de actualizaciones de ubicación para eso).
Por ejemplo, supongamos que el usuario cierra sesión en su aplicación basada en la ubicación y empuja la aplicación a un segundo plano con el botón de inicio. En este caso, su aplicación puede seguir ejecutándose, pero podría tener sentido salir por completo. Sería bueno para el usuario (libera memoria y otros recursos que no es necesario usar), y bueno para la estabilidad de la aplicación (es decir, asegurarse de que la aplicación se reinicie periódicamente cuando sea posible es una red de seguridad contra pérdidas de memoria y otra poca memoria) cuestiones).
Esto podría (aunque probablemente no debería, ver a continuación :-) lograrse con algo como:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
exit(0);
} else {
// normal handling.
}
}
Dado que la aplicación saldría del fondo , el usuario no se verá mal y no se parecerá a un bloqueo, siempre que la interfaz de usuario se restablezca la próxima vez que ejecute la aplicación. En otras palabras, para el usuario no se vería diferente a la terminación de la aplicación iniciada por el sistema cuando la aplicación está en segundo plano.
Aún así, sería preferible utilizar un enfoque más estándar para que el sistema sepa que la aplicación se puede finalizar. Por ejemplo, en este caso, al asegurarse de que el GPS no esté en uso, deje de solicitar actualizaciones de ubicación, incluida la desactivación de mostrar la ubicación actual en una vista de mapa si está presente. De esa manera, el sistema se encargará de finalizar la aplicación unos minutos (es decir [[UIApplication sharedApplication] backgroundTimeRemaining]
) después de que la aplicación ingrese al fondo. Esto obtendría los mismos beneficios sin tener que usar el código para finalizar la aplicación.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
// stop requesting location updates if not already done so
// tidy up as app will soon be terminated (run a background task using beginBackgroundTaskWithExpirationHandler if needed).
} else {
// normal handling.
}
}
Y, por supuesto, el uso exit(0)
nunca sería apropiado para la aplicación de producción promedio que se ejecuta en primer plano, según otras respuestas que hacen referencia a http://developer.apple.com/iphone/library/qa/qa2008/qa1561.html