iPhone アプリをプログラミングしていますが、特定のユーザー アクションのために強制的に終了させる必要があります。アプリが割り当てたメモリをクリーンアップした後、アプリケーションを終了するために呼び出す適切なメソッドは何ですか?
24 に答える
iPhone では、アプリを終了するという概念はありません。アプリを終了させる唯一のアクションは、電話のホーム ボタンに触れることであり、これは開発者がアクセスできるものではありません。
Apple によると、アプリは勝手に終了するべきではありません。ユーザーはホーム ボタンを押していないため、ホーム画面に戻ると、アプリがクラッシュしたような印象をユーザーに与えます。これは紛らわしい非標準の動作であり、回避する必要があります。
試しましたexit(0)
か?
あるいは、[[NSThread mainThread] exit]
私は試していませんが、より適切な解決策のようです。
ここで Q&A を確認してください: https://developer.apple.com/library/content/qa/qa1561/_index.html
Q: プログラムで iOS アプリケーションを終了するにはどうすればよいですか?
iOS アプリケーションを正常に終了するための API は提供されていません。
iOS では、ユーザーはホーム ボタンを押してアプリケーションを閉じます。アプリケーションが意図した機能を提供できない状況にある場合、推奨されるアプローチは、問題の性質と、ユーザーが実行できる可能性のあるアクション (WiFi をオンにする、位置情報サービスを有効にするなど) を示すアラートをユーザーに表示することです。ユーザーが自分の判断でアプリケーションを終了できるようにします。
警告:関数を呼び出さないでください
exit
。アプリケーションの呼び出しexit
は、適切な終了を実行してホーム画面にアニメーションで戻るのではなく、クラッシュしたようにユーザーに表示されます。
-applicationWillTerminate:
さらに、exit を呼び出すと同様のUIApplicationDelegate
メソッドが呼び出されないため、データが保存されない場合があります。開発中またはテスト中にアプリケーションを終了する必要がある場合は、
abort
関数またはassert
マクロを使用することをお勧めします
プログラムをやめる方法ではなく、強制的にやめさせる方法です。
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];
info.plistに移動し、「アプリケーションがバックグラウンドで実行されない」キーを確認します。今回、ユーザーがホームボタンをクリックすると、アプリケーションは完全に終了します。
UIApplicationExitsOnSuspend
にプロパティを追加application-info.plist
しますtrue
。
いくつかのテストの後、私は次のように言うことができます:
- プライベートインターフェースを使用
[UIApplication sharedApplication]
すると、アプリがクラッシュしたように見えますが、- (void)applicationWillTerminate:(UIApplication *)application
そうする前に呼び出します。 - を使用
exit(0);
するとアプリケーションも終了しますが、「通常」に見えます(スプリングボードのアイコンは期待どおりに表示され、ズームアウト効果があります)が、- (void)applicationWillTerminate:(UIApplication *)application
デリゲートメソッドは呼び出されません。
私のアドバイス:
- (void)applicationWillTerminate:(UIApplication *)application
デリゲートを手動で呼び出します。- を呼び出し
exit(0);
ます。
ApplicationDelegate は、ユーザーによる意図的な終了の通知を受け取ります。
- (void)applicationWillResignActive:(UIApplication *)application {
この通知を受け取ったら、電話するだけです
exit(0);
これがすべての作業を行います。そして最高のことは、それはユーザーがやめようとする意図であることです。
私の Audio-App では、音楽がまだ再生されている間に人々がデバイスを同期した後、アプリを終了する必要がありました。同期が完了するとすぐに通知が届きます。しかし、その直後にアプリを終了すると、実際にはクラッシュのように見えます.
その代わりに、次のバックグラウンド アクションでアプリを本当に終了するようにフラグを設定しました。同期後にアプリを更新しても問題ありません。
アップルは次のように述べています。
「警告: exit 関数を呼び出さないでください。exit を呼び出すアプリケーションは、適切な終了を実行してホーム画面にアニメーションで戻るのではなく、ユーザーにはクラッシュしたように見えます。」
これは悪い仮定だと思います。ユーザーが終了ボタンをタップして、「アプリケーションは終了します」のようなメッセージが表示された場合、クラッシュしているようには見えません。Apple は、(exit(0) ではなく) アプリケーションを終了する有効な方法を提供する必要があります。
文書化されていない方法を使用したため、最近アプリが拒否されました。文字通り:
「残念ながら、プライベート API を使用しているため、App Store に追加できません。非公開 API の使用は、iPhone 開発者プログラム ライセンス契約のセクション 3.3.1 で概説されているように禁止されています。
「3.3.1 アプリケーションは、Apple が規定する方法でのみ文書化された API を使用することができ、プライベート API を使用または呼び出してはなりません。」
アプリケーションに含まれている非公開 API は terminateWithSuccess です」
exit(0)
アプリケーションがすぐに終了し、アプリがクラッシュしたように見えるため、この関数を直接呼び出さないでください。そのため、ユーザーに確認アラートを表示して、ユーザーが自分でこれを実行できるようにすることをお勧めします。
スウィフト 4.2
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()
}
}
これは良い答えを得ましたが、少し拡張することにしました:
Apple の iOS ヒューマン インターフェイス ガイドラインをよく読まずに、アプリケーションを AppStore に受け入れてもらうことはできません。(彼らは、彼らに対して何かをしたことであなたを拒否する権利を保持します)セクション「プログラムで終了しないでください」http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices。 html は、この場合の扱い方の正確なガイドラインです。
Apple プラットフォームに問題があり、解決策が簡単に見つからない場合は、HIG に相談してください。Apple があなたにそれをしてほしくないという可能性もあり、彼らは通常 (私は Apple ではないので、常に保証はできません) 彼らのドキュメントでそう言っています。
たとえば、アプリケーションがインターネット接続を必要とする場合、アプリケーションを「終了する必要がある」場合があります。アラートを表示してから、次のようにすることができます。
if ([[UIApplication sharedApplication] respondsToSelector:@selector(terminate)]) {
[[UIApplication sharedApplication] performSelector:@selector(terminate)];
} else {
kill(getpid(), SIGINT);
}
上記に加えて、良い答えを付け加えたいと思います。あなたの記憶をきれいにすることを考えてください。
アプリケーションが終了すると、iPhone OSはアプリケーションに残っているものをすべて自動的にクリーンアップするため、すべてのメモリを手動で解放すると、アプリケーションの終了にかかる時間が長くなる可能性があります。
- (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);
}
}
上記の [[NSMutableArray new] addObject:nil] アプローチを使用して、明確な exit(0) 関数呼び出しを行わずにアプリを強制終了 (クラッシュ) しました。
なんで?私のアプリは、すべてのネットワーク API 呼び出しで証明書のピン留めを使用して、中間者攻撃を防ぐためです。これらには、金融アプリが起動時に行う初期化呼び出しが含まれます。
証明書認証が失敗すると、すべての初期化呼び出しがエラーになり、アプリが不確定な状態になります。ユーザーが家に帰ってからアプリに戻っても、OS によってアプリが消去されない限り、まだ初期化されておらず、信頼できないため、役に立ちません。
したがって、この 1 つのケースでは、アプリが安全でない環境で動作していることをユーザーに通知するアラートをポップし、ユーザーが [閉じる] をクリックしたときに、前述の方法を使用してアプリを強制終了するのが最善であると判断しました。
[[UIApplication sharedApplication] terminateWithSuccess];
それは正常に機能し、自動的に呼び出します
- (void)applicationWillTerminateUIApplication *)application delegate.
コンパイル時の警告を削除するには、このコードを追加します
@interface UIApplication(MyExtras)
- (void)terminateWithSuccess;
@end
アプリをいつ終了するかは、ユーザーが決定する必要があります。アプリが終了したときのユーザー インタラクションは適切ではないと思います。そのため、適切な API はありません。ホーム ボタンだけに API があります。
エラーがある場合: 改善するか、ユーザーに通知します。再起動が必要な場合: ユーザーに通知するよりも適切に実装します。
ばかげているように聞こえますが、ユーザーに決定させたり、通知したりせずにアプリを終了するのは悪い習慣です。また、ユーザー インタラクション用のホーム ボタンがあるため、Apple は、同じ機能 (アプリの終了) に 2 つのボタンを使用するべきではないと述べています。
たとえば、位置情報の更新を取得するために (位置情報の更新のバックグラウンド機能を使用して)、アプリがバックグラウンドでも実行される長期間存続するアプリである場合は、アプリを終了することが適切な場合があります。
たとえば、ユーザーが位置情報ベースのアプリからログアウトし、ホーム ボタンを使用してアプリをバックグラウンドにプッシュしたとします。この場合、アプリは実行し続ける可能性がありますが、完全に終了することが理にかなっている場合があります。これは、ユーザーにとっても (使用する必要のないメモリやその他のリソースを解放する)、アプリの安定性にも役立ちます (つまり、可能な場合にアプリを定期的に再起動することは、メモリ リークやその他のメモリ不足に対するセーフティ ネットです)。問題)。
これは、次のような方法で実現できます (おそらくそうすべきではありませんが、以下を参照してください :-):
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
exit(0);
} else {
// normal handling.
}
}
その後、アプリはバックグラウンドから終了するため、次回アプリを実行したときにユーザー インターフェイスが復元されていれば、ユーザーにとっては間違っているように見えず、クラッシュのようにも見えません。つまり、ユーザーにとっては、アプリがバックグラウンドにあるときにシステムが開始したアプリの終了と何ら変わりはありません。
それでも、アプリを終了できることをシステムに知らせるには、より標準的な方法を使用することをお勧めします。たとえば、この場合、GPS が使用されていないことを確認して、位置の更新の要求を停止します。これには、マップ ビューに現在の位置が表示されている場合はそれをオフにすることも含まれます。そうすれば、アプリがバックグラウンドに入ってから数分後 (つまり[[UIApplication sharedApplication] backgroundTimeRemaining]
)、システムがアプリを終了します。これにより、アプリを終了するコードを使用しなくても、同じ利点がすべて得られます。
- (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.
}
}
そしてもちろん、http://developer.apple.com/iphone/library/qa/qa2008/qa1561.htmlexit(0)
を参照する他の回答によると、フォアグラウンドで実行される平均的な本番アプリには決して適切ではありません。
Swift 4.2 (またはそれ以前)
呼び出されたライブラリDarvin
を使用できます。
import Darwin
exit(0) // Here you go
注意: これは iOS アプリケーションでは推奨されません。
これを行うと、クラッシュ ログが取得されます。