Estou programando um aplicativo para iPhone e preciso forçá-lo a sair devido a determinadas ações do usuário. Após limpar a memória que o aplicativo alocou, qual é o método apropriado a ser chamado para encerrar o aplicativo?
Estou programando um aplicativo para iPhone e preciso forçá-lo a sair devido a determinadas ações do usuário. Após limpar a memória que o aplicativo alocou, qual é o método apropriado a ser chamado para encerrar o aplicativo?
Respostas:
Você já tentou exit(0)
?
Como alternativa, [[NSThread mainThread] exit]
embora eu não tenha tentado, parece a solução mais apropriada.
No iPhone, não existe o conceito de sair de um aplicativo. A única ação que deve fazer com que um aplicativo seja fechado é tocar no botão Início no telefone, e não é a isso que os desenvolvedores têm acesso.
De acordo com a Apple, seu aplicativo não deve terminar sozinho. Como o usuário não pressionou o botão Início, qualquer retorno à tela inicial dá ao usuário a impressão de que seu aplicativo travou. Esse é um comportamento confuso e não padrão e deve ser evitado.
exit (0) aparece para um usuário como falhas, então mostre uma mensagem de confirmação para o usuário. Após a confirmação, suspenda (pressione o botão home programaticamente) e aguarde 2 segundos enquanto o aplicativo está em segundo plano com animação e saia da visualização do usuário
-(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)
não importa. Point é que seu aplicativo tem "comportamento de abandono". O comportamento de desistência é proibido na AppStore, exceto alguns aplicativos criados por terceiros muito importantes. Além disso, imitar o comportamento do botão home também está sujeito a ser rejeitado.
Verifique as perguntas e respostas aqui: https://developer.apple.com/library/content/qa/qa1561/_index.html
P: Como encerro programaticamente meu aplicativo iOS?
Não há API fornecida para finalizar normalmente um aplicativo iOS.
No iOS, o usuário pressiona o botão Início para fechar os aplicativos. Caso seu aplicativo tenha condições em que não possa fornecer a função pretendida, a abordagem recomendada é exibir um alerta para o usuário que indique a natureza do problema e as possíveis ações que o usuário poderá executar - ativando o Wi-Fi, ativando os Serviços de Localização etc. Permita que o usuário encerre o aplicativo a seu critério.
AVISO: Não chame a
exit
função. Os aplicativos que chamamexit
parecerão que o usuário falhou, em vez de executar uma finalização normal e voltar à tela inicial.Além disso, os dados podem não ser salvos, porque
-applicationWillTerminate:
e similaresUIApplicationDelegate
métodos não serão chamados se você chamar exit.Se durante o desenvolvimento ou teste for necessário encerrar o aplicativo, a
abort
função ouassert
macro é recomendada
Não é realmente uma maneira de sair do programa, mas uma maneira de forçar as pessoas a sair.
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];
Vá para o seu info.plist e marque a tecla "O aplicativo não é executado em segundo plano". Desta vez, quando o usuário clica no botão inicial, o aplicativo sai completamente.
Adicione UIApplicationExitsOnSuspend
propriedade application-info.plist
a true
.
Após alguns testes, posso dizer o seguinte:
[UIApplication sharedApplication]
fará com que o aplicativo pareça travar, mas chamará- (void)applicationWillTerminate:(UIApplication *)application
antes de fazer isso;exit(0);
também encerrará o aplicativo, mas parecerá "normal" (os ícones do trampolim aparecerão como o esperado, com o efeito de redução de zoom), MAS não chamará o - (void)applicationWillTerminate:(UIApplication *)application
método delegado.Meu conselho:
- (void)applicationWillTerminate:(UIApplication *)application
para o delegado.exit(0);
.O ApplicationDelegate é notificado sobre a saída intencional do usuário:
- (void)applicationWillResignActive:(UIApplication *)application {
Quando recebo esta notificação, ligo apenas
exit(0);
O que faz todo o trabalho. E o melhor é que os usuários pretendem sair, e é por isso que não deve ser um problema chamá-lo para lá.
No meu aplicativo de áudio, era necessário sair do aplicativo depois que as pessoas sincronizavam seus dispositivos enquanto a música ainda estava tocando. Assim que a sincronização é concluída, recebo uma notificação. Mas sair do aplicativo logo depois pareceria um acidente.
Então, em vez disso, defino um sinalizador para REALMENTE sair do aplicativo na próxima ação de segundo plano. Não há problema em atualizar o aplicativo após uma sincronização.
Meu aplicativo foi rejeitado recentemente porque usei um método não documentado. Literalmente:
"Infelizmente, ele não pode ser adicionado à App Store porque está usando uma API privada. É proibido o uso de APIs não públicas, conforme descrito na seção 3.3.1 do Contrato de licença do programa para desenvolvedores do iPhone:
"3.3.1 Os aplicativos podem usar APIs documentadas apenas da maneira prescrita pela Apple e não devem usar ou chamar APIs privadas."
A API não pública incluída no seu aplicativo é terminateWithSuccess "
A Apple diz:
"Aviso: não chame a função exit. Os aplicativos que chamam exit parecerão que o usuário travou, em vez de executar uma finalização normal e animar de volta para a tela inicial."
Eu acho que isso é uma suposição ruim. Se o usuário tocar em um botão sair e aparecer uma mensagem dizendo algo como: "O aplicativo agora será encerrado.", Ele não parece estar travado. A Apple deve fornecer uma maneira válida de encerrar um aplicativo (não sair (0)).
Isso obteve uma boa resposta, mas decidiu expandir um pouco:
Você não pode aceitar seu aplicativo na AppStore sem ler bem as Diretrizes de interface humana para iOS da Apple. (eles se reservam o direito de rejeitá-lo por fazer algo contra eles) A seção "Não saia programaticamente" http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices. html é uma orientação exata sobre como você deve tratar neste caso.
Se você tiver algum problema com a plataforma Apple para o qual não consegue encontrar facilmente uma solução, consulte a HIG. É possível que a Apple simplesmente não queira que você faça isso e eles geralmente (eu não sou a Apple, por isso não posso garantir sempre) o fazem na documentação.
Hm, talvez você precise sair do aplicativo se, digamos, seu aplicativo exigir uma conexão com a Internet. Você pode exibir um alerta e, em seguida, fazer algo assim:
if ([[UIApplication sharedApplication] respondsToSelector:@selector(terminate)]) {
[[UIApplication sharedApplication] performSelector:@selector(terminate)];
} else {
kill(getpid(), SIGINT);
}
Não podemos deixar de aplicativo usando exit(0)
, abort()
funções, como a Apple fortemente desencorajar o uso dessas funções. Embora você possa usar essas funções para fins de desenvolvimento ou teste.
Se, durante o desenvolvimento ou teste, for necessário encerrar o aplicativo, é recomendável a função de abortar ou a macro de afirmação
Por favor, encontre este Q&A da Apple tópico de para obter mais informações.
Conforme o uso dessa função, crie uma impressão como se o aplicativo estivesse travando. Então, recebi uma sugestão: podemos exibir Alerta com mensagem de encerramento para o usuário ciente sobre o fechamento do aplicativo, devido à indisponibilidade de determinadas funcionalidades.
Porém, as diretrizes da interface humana do iOS para iniciar e parar o aplicativo sugerem que nunca use o botão Quit ou Close para encerrar o aplicativo. Em vez disso, eles estão sugerindo exibir uma mensagem adequada para explicar a situação.
Um aplicativo iOS nunca exibe uma opção Fechar ou Sair. As pessoas param de usar um aplicativo quando alternam para outro aplicativo, retornam à tela inicial ou colocam seus dispositivos no modo de suspensão.
Nunca saia de um aplicativo iOS programaticamente. As pessoas tendem a interpretar isso como um acidente. Se algo impedir o funcionamento do aplicativo conforme o esperado, você precisará informar aos usuários sobre a situação e explicar o que eles podem fazer sobre isso.
Além do que foi dito acima, boa resposta que eu gostaria de acrescentar, pense em limpar sua memória.
Depois que o aplicativo é encerrado, o iPhone OS limpa automaticamente tudo o que for deixado para trás. Portanto, liberar toda a memória manualmente pode aumentar a quantidade de tempo que o aplicativo leva para sair.
- (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);
}
}
Usei a abordagem [[NSMutableArray new] addObject: nil] mencionada acima para forçar o encerramento (travamento) do aplicativo sem fazer uma chamada de função de saída de revelador (0).
Por quê? Porque meu aplicativo usa a fixação de certificados em todas as chamadas de API da rede para impedir ataques intermediários. Isso inclui as chamadas de inicialização que meu aplicativo financeiro faz na inicialização.
Se a autenticação do certificado falhar, toda a minha inicialização chamará o erro e deixará meu aplicativo em um estado indeterminado. Deixar o usuário ir para casa e depois voltar para o aplicativo não ajuda, pois, a menos que o aplicativo tenha sido eliminado pelo sistema operacional, ele ainda não foi inicializado e não é confiável.
Portanto, nesse caso, consideramos melhor exibir um alerta informando ao usuário que o aplicativo está operando em um ambiente inseguro e, quando eles pressionam "Fechar", forçam o encerramento do aplicativo usando o método mencionado acima.
[[UIApplication sharedApplication] terminateWithSuccess];
Funcionou bem e chama automaticamente
- (void)applicationWillTerminateUIApplication *)application delegate.
para remover o aviso de tempo de compilação, adicione este código
@interface UIApplication(MyExtras)
- (void)terminateWithSuccess;
@end
Você não deve chamar diretamente a função exit(0)
pois ela encerrará o aplicativo imediatamente e parecerá que seu aplicativo falhou. É melhor mostrar aos usuários um alerta de confirmação e permitir que eles façam isso sozinhos.
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()
}
}
O usuário deve decidir quando um aplicativo sai. Não acho que seja uma boa interação do usuário quando um aplicativo é encerrado. Portanto, não há uma API agradável para isso, apenas o botão home possui uma.
Se houver um erro: Implemente melhor ou Notifique o usuário. Se for necessário reiniciar: implemente melhor Notificar o usuário.
Parece idiota, mas é uma prática recomendada sair do aplicativo sem permitir que o usuário decida e sem notificá-lo. E como há um botão home para a interação do usuário, afirma a Apple, não deve haver duas coisas para a mesma função (saindo de um aplicativo).
Sair de um aplicativo de outra maneira que não o botão de início é realmente uma abordagem que não é do iOS .
Eu fiz esse ajudante, no entanto, que não usa coisas particulares:
void crash()
{ [[NSMutableArray new] addObject:NSStringFromClass(nil)]; }
Mas ainda não destinado à produção no meu caso. É para testar relatórios de falhas ou para reiniciar rapidamente após uma redefinição dos Dados Principais. Apenas tornou seguro não ser rejeitado se a função permanecesse no código de produção.
No iPadOS 13, agora você pode fechar todas as sessões de cena como esta:
for session in UIApplication.shared.openSessions {
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
}
Isso chamará applicationWillTerminate(_ application: UIApplication)
o delegado do aplicativo e o encerrará no final.
Mas cuidado com duas coisas:
Certamente não se destina a ser usado para fechar todas as cenas. (consulte https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/multiple-windows/ )
Compila e roda bem no iOS 13 em um iPhone, mas parece não fazer nada.
Mais informações sobre cenas no iOS / iPadOS 13: https://developer.apple.com/documentation/uikit/app_and_environment/scenes
Pode ser apropriado sair de um aplicativo se for um aplicativo de longa duração que também seja executado em segundo plano, por exemplo, para obter atualizações de local (usando as atualizações de local recurso de segundo plano de para isso).
Por exemplo, digamos que o usuário efetue logout do seu aplicativo com base em localização e empurre o aplicativo para segundo plano usando o botão home. Nesse caso, seu aplicativo pode continuar em execução, mas pode fazer sentido sair completamente dele. Seria bom para o usuário (libera memória e outros recursos que não precisam ser usados) e bom para a estabilidade do aplicativo (ou seja, garantir que o aplicativo seja reiniciado periodicamente sempre que possível, é uma rede de segurança contra vazamentos de memória e outros problemas de memória insuficiente problemas).
Isso poderia (embora provavelmente não deva, veja abaixo :-) ser alcançado com algo como:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
exit(0);
} else {
// normal handling.
}
}
Como o aplicativo sairia do segundo plano ele não parecerá errado para o usuário e não se parecerá com uma falha, desde que a interface do usuário seja restaurada na próxima vez em que o aplicativo for executado. Em outras palavras, para o usuário, não pareceria diferente para o encerramento do aplicativo iniciado pelo sistema quando o aplicativo estiver em segundo plano.
Ainda assim, seria preferível usar uma abordagem mais padrão para informar ao sistema que o aplicativo pode ser encerrado. Por exemplo, neste caso, certificando-se de que o GPS não está em uso, parando de solicitar atualizações de localização, incluindo desativar a exibição da localização atual em uma visualização de mapa, se presente. Dessa forma, o sistema cuidará de encerrar o aplicativo alguns minutos (ie [[UIApplication sharedApplication] backgroundTimeRemaining]
) após o aplicativo entrar em segundo plano. Isso teria os mesmos benefícios sem a necessidade de usar o código para finalizar o aplicativo.
- (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.
}
}
E, é claro, o uso exit(0)
nunca seria apropriado para o aplicativo de produção médio executado em primeiro plano, conforme outras respostas que fazem referência a http://developer.apple.com/iphone/library/qa/qa2008/qa1561.html