Programuję aplikację na iPhone'a i muszę wymusić jej zamknięcie z powodu pewnych działań użytkownika. Po wyczyszczeniu pamięci przydzielonej aplikacji, jaką metodę należy wywołać, aby zakończyć działanie aplikacji?
Programuję aplikację na iPhone'a i muszę wymusić jej zamknięcie z powodu pewnych działań użytkownika. Po wyczyszczeniu pamięci przydzielonej aplikacji, jaką metodę należy wywołać, aby zakończyć działanie aplikacji?
Odpowiedzi:
Próbowałeś exit(0)
?
Alternatywnie, [[NSThread mainThread] exit]
chociaż nie próbowałem, że wydaje się to bardziej odpowiednie rozwiązanie.
Na iPhonie nie ma pojęcia o zamknięciu aplikacji. Jedynym działaniem, które powinno spowodować zamknięcie aplikacji, jest dotknięcie przycisku Początek na telefonie, a programiści nie mają do tego dostępu.
Według Apple'a twoja aplikacja nie powinna zakończyć się sama. Ponieważ użytkownik nie nacisnął przycisku strony głównej, każdy powrót do ekranu głównego powoduje wrażenie, że aplikacja uległa awarii. Jest to mylące, niestandardowe zachowanie, którego należy unikać.
wyjście (0) pojawia się użytkownikowi jako awaria, więc pokaż mu komunikat potwierdzający. Po potwierdzeniu wstrzymaj (przycisk Home naciśnij programowo) i poczekaj 2 sekundy, aż aplikacja przejdzie w tło z animacją, a następnie wyjdź za widok użytkownika
-(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)
nie ma znaczenia. Chodzi o to, że Twoja aplikacja „zachowuje się podczas rzucania”. Samo rzucanie zachowania jest zabronione w AppStore, z wyjątkiem kilku aplikacji tworzonych przez bardzo ważne firmy zewnętrzne. Również naśladowanie zachowania przycisku Home również podlega odrzuceniu.
Sprawdź pytania i odpowiedzi tutaj: https://developer.apple.com/library/content/qa/qa1561/_index.html
P: Jak programowo wyjść z aplikacji na iOS?
Nie ma interfejsu API umożliwiającego pełne zamknięcie aplikacji na iOS.
W systemie iOS użytkownik naciska przycisk Home, aby zamknąć aplikacje. Jeśli aplikacja ma warunki, w których nie może zapewnić swojej zamierzonej funkcji, zalecanym podejściem jest wyświetlenie ostrzeżenia dla użytkownika, wskazującego charakter problemu i możliwe działania, które użytkownik może podjąć - włączenie Wi-Fi, włączenie usług lokalizacyjnych itp. Pozwól użytkownikowi zakończyć aplikację według własnego uznania.
OSTRZEŻENIE: Nie wywoływaj
exit
funkcji. Wywołanie aplikacjiexit
będzie wyglądało na to, że użytkownik się zawiesił, zamiast wykonywania pełnego wdzięku zakończenia i animowania z powrotem do ekranu głównego.Ponadto dane mogą nie zostać zapisane, ponieważ
-applicationWillTerminate:
i podobneUIApplicationDelegate
metody nie zostaną wywołane, jeśli wywołasz exit.Jeśli podczas programowania lub testowania konieczne jest zakończenie aplikacji, zalecana jest
abort
funkcja lubassert
makro
To nie jest tak naprawdę sposób na wyjście z programu, ale sposób zmuszenia ludzi do wyjścia.
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];
Przejdź do swojej info.plist i sprawdź klucz „Aplikacja nie działa w tle”. Tym razem, gdy użytkownik kliknie przycisk Początek, aplikacja zostanie całkowicie zamknięta.
Dodaj UIApplicationExitsOnSuspend
właściwość application-info.plist
do true
.
Po kilku testach mogę powiedzieć:
[UIApplication sharedApplication]
spowoduje, że aplikacja będzie wyglądała na zawieszoną, ALE zadzwoni, - (void)applicationWillTerminate:(UIApplication *)application
zanim to zrobi;exit(0);
spowoduje również zamknięcie aplikacji, ale będzie wyglądać „normalnie” (ikony trampoliny wyglądają zgodnie z oczekiwaniami, z efektem pomniejszenia), ALE nie wywoła - (void)applicationWillTerminate:(UIApplication *)application
metody delegowania.Moja rada:
- (void)applicationWillTerminate:(UIApplication *)application
do delegata.exit(0);
.Twoja aplikacjaDelegate otrzymuje powiadomienie o celowym zamknięciu przez użytkownika:
- (void)applicationWillResignActive:(UIApplication *)application {
Kiedy otrzymam to powiadomienie, po prostu dzwonię
exit(0);
Który wykonuje całą pracę. A najlepsze jest to, że useres zamierza rzucić palenie, dlatego nie powinno być problemu z nazwaniem go tam.
W mojej aplikacji audio konieczne było zamknięcie aplikacji po zsynchronizowaniu urządzenia podczas odtwarzania muzyki. Po zakończeniu synchronizacji otrzymuję powiadomienie. Ale zamknięcie aplikacji zaraz po tym wyglądałoby jak awaria.
Zamiast tego ustawiłem flagę, aby NAPRAWDĘ zamknąć aplikację podczas następnej akcji w tle. Co jest w porządku do odświeżenia aplikacji po synchronizacji.
Moja aplikacja została ostatnio odrzucona, ponieważ użyłem nieudokumentowanej metody. Dosłownie:
„Niestety nie można go dodać do App Store, ponieważ korzysta z prywatnego API. Korzystanie z niepublicznych API, które jest opisane w Umowie licencyjnej na program dla programistów iPhone, sekcja 3.3.1 jest zabronione:
„3.3.1 Aplikacje mogą korzystać z udokumentowanych interfejsów API wyłącznie w sposób określony przez Apple i nie mogą używać ani wywoływać żadnych prywatnych interfejsów API”.
Niepubliczny interfejs API dołączony do aplikacji to terminateWithSuccess ”
Apple mówi:
„Ostrzeżenie: nie wywoływaj funkcji wyjścia. Aplikacje wywołujące wyjście będą się wyświetlać jako zawieszone, zamiast wykonywać pełne zakończenie działania i animować z powrotem do ekranu głównego.”
Myślę, że to złe założenie. Jeśli użytkownik stuknie przycisk wyjścia, a pojawi się komunikat z napisem: „Aplikacja zostanie teraz zamknięta”, oznacza to, że nie jest zawieszony. Apple powinien zapewnić prawidłowy sposób na zamknięcie aplikacji (nie wychodzić (0)).
Otrzymał dobrą odpowiedź, ale postanowił nieco rozszerzyć:
Nie możesz zaakceptować swojej aplikacji w AppStore bez dobrego przeczytania Wytycznych Apple dotyczących interfejsu ludzkiego iOS. (zachowują prawo do odrzucenia cię za cokolwiek przeciwko nim). Sekcja „Nie wychodź programowo” http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices. HTML jest dokładną wskazówką, jak należy postępować w tym przypadku.
Jeśli kiedykolwiek masz problem z platformą Apple, na którą nie możesz łatwo znaleźć rozwiązania, skonsultuj się z HIG. Jest możliwe, że Apple po prostu nie chce, abyś to zrobił i zwykle (nie jestem Apple, więc nie mogę zagwarantować, że zawsze), mówią to w swojej dokumentacji.
Hm, może być konieczne „zamknięcie” aplikacji, jeśli powiedzmy, że aplikacja wymaga połączenia z Internetem. Możesz wyświetlić alert, a następnie zrobić coś takiego:
if ([[UIApplication sharedApplication] respondsToSelector:@selector(terminate)]) {
[[UIApplication sharedApplication] performSelector:@selector(terminate)];
} else {
kill(getpid(), SIGINT);
}
Nie możemy zamknąć aplikację za pomocą exit(0)
, abort()
funkcje, jak Apple zdecydowanie zniechęcać do korzystania z tych funkcji. Chociaż możesz użyć tej funkcji do celów programistycznych lub testowych.
Jeśli podczas programowania lub testowania konieczne jest zakończenie aplikacji, zaleca się funkcję przerwania lub makro makra
Znajdź ten wątek Apple Pytania i odpowiedzi, aby uzyskać więcej informacji.
Korzystanie z tej funkcji powoduje wrażenie, jakby aplikacja ulegała awarii. Mam więc sugestię, że możemy wyświetlić komunikat Alert z zakończeniem, aby poinformować użytkownika o zamknięciu aplikacji z powodu niedostępności niektórych funkcji.
Ale iOS Human Interface Interface Wytyczne dotyczące uruchamiania i zatrzymywania aplikacji , sugerujące, że Nigdy nie używaj przycisku Quit lub Close, aby zakończyć aplikację. Zamiast tego sugerują wyświetlenie odpowiedniego komunikatu wyjaśniającego sytuację.
Aplikacja na iOS nigdy nie wyświetla opcji Zamknij ani Wyjdź. Ludzie przestają korzystać z aplikacji, gdy przechodzą do innej aplikacji, wracają do ekranu głównego lub przełączają urządzenia w tryb uśpienia.
Nigdy nie zamykaj programowo aplikacji na iOS. Ludzie mają tendencję do interpretowania tego jako katastrofy. Jeśli coś uniemożliwi działanie aplikacji zgodnie z przeznaczeniem, musisz poinformować użytkowników o sytuacji i wyjaśnić, co mogą z tym zrobić.
Oprócz powyższego, dobrze, odpowiedź, którą chciałem dodać, pomyśl o oczyszczeniu pamięci.
Po zamknięciu aplikacji iPhone OS automatycznie wyczyści wszystko, co pozostawiła aplikacja, więc ręczne zwolnienie całej pamięci może tylko wydłużyć czas potrzebny na zamknięcie aplikacji.
- (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);
}
}
Użyłem wspomnianego powyżej [[NSMutableArray new] addObject: nil], aby wymusić zamknięcie aplikacji (awarię) bez wykonywania funkcji kontrolnej exit (0).
Czemu? Ponieważ moja aplikacja używa przypinania certyfikatów do wszystkich wywołań interfejsu API sieci, aby zapobiec atakom typu man-in-the-middle. Obejmują one połączenia inicjalizacyjne, które moja aplikacja finansowa wykonuje podczas uruchamiania.
Jeśli uwierzytelnianie certyfikatu się nie powiedzie, wszystkie moje wywołania inicjujące powodują błąd i pozostawiają moją aplikację w nieokreślonym stanie. Pozwalanie użytkownikowi wrócić do domu, a następnie wrócić do aplikacji, nie pomaga, ponieważ dopóki aplikacja nie zostanie wyczyszczona przez system operacyjny, nadal nie jest inicjowana i nie jest godna zaufania.
Dlatego w tym jednym przypadku uznaliśmy, że najlepiej jest wysłać alert informujący użytkownika, że aplikacja działa w niepewnym środowisku, a następnie, gdy naciśnie przycisk „Zamknij”, wymuś zamknięcie aplikacji przy użyciu wyżej wymienionej metody.
[[UIApplication sharedApplication] terminateWithSuccess];
Działa dobrze i automatycznie dzwoni
- (void)applicationWillTerminateUIApplication *)application delegate.
aby usunąć ostrzeżenie o czasie kompilacji, dodaj ten kod
@interface UIApplication(MyExtras)
- (void)terminateWithSuccess;
@end
Nie powinieneś bezpośrednio wywoływać funkcji, exit(0)
ponieważ natychmiast zamknie aplikację i będzie wyglądać, jakby aplikacja uległa awarii. Lepiej więc pokazać użytkownikom alert z potwierdzeniem i pozwolić im to zrobić sami.
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()
}
}
Użytkownik powinien zdecydować, kiedy aplikacja zostanie zamknięta. Nie sądzę, że jest to dobra interakcja użytkownika, gdy aplikacja kończy pracę. Dlatego nie ma dla niego dobrego API, tylko przycisk Home ma taki.
Jeśli wystąpi błąd: lepiej go zaimplementuj lub powiadom użytkownika. Jeśli konieczne jest ponowne uruchomienie komputera: zaimplementuj go lepiej, powiadamiając użytkownika.
Brzmi głupio, ale złą praktyką jest wyjście z aplikacji bez pozwolenia użytkownikowi na powiadomienie. A ponieważ Apple ma przycisk Home do interakcji z użytkownikiem, nie powinno być dwóch rzeczy dla tej samej funkcji (wychodzenie z aplikacji).
Wyjście z aplikacji w inny sposób niż przycisk home, jest naprawdę nie-iOS .
Zrobiłem jednak tego pomocnika, który nie używa żadnych prywatnych rzeczy:
void crash()
{ [[NSMutableArray new] addObject:NSStringFromClass(nil)]; }
Ale nadal nie jest przeznaczony do produkcji w moim przypadku. Służy do testowania raportów o awariach lub szybkiego restartu po resecie danych podstawowych. Po prostu zabezpieczyłem go przed odrzuceniem, jeśli funkcja pozostanie w kodzie produkcyjnym.
W iPadOS 13 możesz teraz zamknąć wszystkie sesje scen w następujący sposób:
for session in UIApplication.shared.openSessions {
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
}
Spowoduje to wezwanie applicationWillTerminate(_ application: UIApplication)
delegata aplikacji i zakończenie aplikacji na końcu.
Ale uwaga na dwie rzeczy:
Z pewnością nie jest to przeznaczone do zamykania wszystkich scen. (patrz https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/multiple-windows/ )
Kompiluje się i działa dobrze na iOS 13 na iPhonie, ale wydaje się, że nic nie robi.
Więcej informacji o scenach w iOS / iPadOS 13: https://developer.apple.com/documentation/uikit/app_and_environment/scenes
Może być właściwe zamknięcie aplikacji, jeśli jest to aplikacja długotrwała, która również działa w tle, na przykład w celu uzyskania aktualizacji lokalizacji (przy użyciu aktualizacji lokalizacji tle).
Załóżmy na przykład, że użytkownik wylogowuje się z aplikacji opartej na lokalizacji i przesuwa aplikację w tło za pomocą przycisku strony głównej. W takim przypadku Twoja aplikacja może nadal działać, ale warto całkowicie ją zamknąć. Byłoby to dobre dla użytkownika (zwalnia pamięć i inne zasoby, które nie muszą być używane), a także poprawia stabilność aplikacji (tj. Upewnianie się, że aplikacja jest okresowo restartowana, gdy jest to możliwe, stanowi zabezpieczenie przed przeciekami pamięci i innymi brakami pamięci zagadnienia).
Można to (choć prawdopodobnie nie powinno, patrz poniżej :-) osiągnąć za pomocą czegoś takiego:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
exit(0);
} else {
// normal handling.
}
}
Ponieważ aplikacja wyjdzie z tła nie będzie wyglądać źle dla użytkownika i nie będzie przypominać awarii, pod warunkiem, że interfejs użytkownika zostanie przywrócony przy następnym uruchomieniu aplikacji. Innymi słowy, dla użytkownika nie wyglądałoby inaczej niż inicjowane przez system zakończenie aplikacji, gdy aplikacja jest w tle.
Nadal jednak lepiej byłoby zastosować bardziej standardowe podejście, aby poinformować system, że aplikacja może zostać zakończona. Na przykład w tym przypadku, upewniając się, że GPS nie jest używany, przerywając żądanie aktualizacji lokalizacji, w tym wyłączając wyświetlanie bieżącej lokalizacji w widoku mapy, jeśli jest obecny. W ten sposób system zajmie się zakończeniem aplikacji kilka minut (tj. [[UIApplication sharedApplication] backgroundTimeRemaining]
) Po wejściu aplikacji w tło. Przyniosłoby to te same korzyści bez konieczności używania kodu w celu zakończenia aplikacji.
- (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.
}
}
I oczywiście używanie exit(0)
nigdy nie byłoby odpowiednie dla przeciętnej aplikacji produkcyjnej działającej na pierwszym planie, zgodnie z innymi odpowiedziami, które odwołują się do http://developer.apple.com/iphone/library/qa/qa2008/qa1561.html