Chcę zatrzymać aplikację w określonym momencie. Innymi słowy, chcę, aby moja aplikacja wykonała kod, ale w pewnym momencie zatrzymaj się na 4 sekundy, a następnie kontynuuj z resztą kodu. W jaki sposób mogę to zrobić?
sen działa bez importowania darwina, nie jestem pewien, czy to zmiana
Dan Beaulieu
17
usleep () również działa, jeśli potrzebujesz czegoś bardziej precyzyjnego niż rozdzielczość 1 sekundy.
prewett
18
usleep () zajmuje milionowe części sekundy, więc usleep (1000000) będzie spał przez 1 sekundę
Harris,
40
Dziękujemy za skrócenie preambuły „nie rób tego”.
Dan Rosenstark,
2
Używam sleep () do symulacji długotrwałych procesów w tle.
William T. Mallard,
345
Użycie dispatch_afterbloku jest w większości przypadków lepsze niż użycie, sleep(time)ponieważ wątek, w którym wykonywany jest sen, jest blokowany przed wykonywaniem innej pracy. podczas korzystania dispatch_afterz wątku, który jest przetwarzany, nie jest blokowany, dzięki czemu może wykonywać inne prace w międzyczasie. Jeśli pracujesz nad głównym wątkiem aplikacji, korzystanie z niego sleep(time)jest niekorzystne dla wygody użytkownika aplikacji, ponieważ interfejs użytkownika nie odpowiada w tym czasie.
Wyślij po zaplanowaniu wykonania bloku kodu zamiast zamrażania wątku:
Szybki ≥ 3,0
let seconds =4.0DispatchQueue.main.asyncAfter(deadline:.now()+ seconds){// Put your code which should be executed with a delay here
}
Szybki <3,0
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW),4*Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()){// Put your code which should be executed with a delay here
}
Zaktualizowano kod dla Xcode 8.2.1. Wcześniej .now() + .seconds(4)podaje błąd:expression type is ambiguous without more context
richards
Jak mogę zawiesić funkcję wywoływaną z kolejki? @Palle
Mukul More
14
Nie trzeba już dodawać, .now() + .seconds(5)że jest po prostu.now() + 5
cnzac
45
Porównanie różnych podejść w swift 3.0
1. Spać
Ta metoda nie ma oddzwonienia. Umieść kody bezpośrednio po tym wierszu, aby wykonać je w 4 sekundy. Powstrzyma użytkownika od iteracji z elementami interfejsu użytkownika, takimi jak przycisk testowy, aż do upływu czasu. Chociaż przycisk jest jakby zamrożony, gdy zaczyna się sen, inne elementy, takie jak wskaźnik aktywności, wciąż się obracają bez zamrażania. Nie możesz ponownie uruchomić tej akcji podczas snu.
sleep(4)
print("done")//Do stuff here
2. Wyślij, wykonaj i stoper
Te trzy metody działają podobnie, wszystkie działają w wątku w tle z oddzwanianiem, tylko z inną składnią i nieco innymi funkcjami.
Polecenie Dispatch jest powszechnie używane do uruchamiania czegoś w wątku w tle. Ma funkcję zwrotną jako część wywołania funkcji
W przypadku wszystkich tych trzech metod kliknięcie przycisku w celu ich uruchomienia powoduje, że interfejs użytkownika nie zawiesza się i można kliknąć go ponownie. Ponowne kliknięcie przycisku powoduje ustawienie innego timera i oddzwanianie zostanie uruchomione dwukrotnie.
Podsumowując
Żadna z czterech metod nie działa wystarczająco dobrze sama z siebie. sleepwyłączy interakcję użytkownika, więc ekran „ zawiesza się ” (nie w rzeczywistości) i powoduje złe wrażenia użytkownika. Pozostałe trzy metody nie zawieszają ekranu, ale możesz je uruchomić wiele razy i przez większość czasu chcesz poczekać, aż odzyskasz połączenie, zanim zezwolisz użytkownikowi na ponowne nawiązanie połączenia.
Tak więc lepszym projektem będzie użycie jednej z trzech metod asynchronicznych z blokowaniem ekranu. Gdy użytkownik kliknie przycisk, zakryj cały ekran półprzezroczystym widokiem ze wskaźnikiem aktywności wirowania u góry, informującym użytkownika, że kliknięcie przycisku jest obsługiwane. Następnie usuń widok i wskaźnik w funkcji oddzwaniania, informując użytkownika, że akcja jest odpowiednio obsługiwana itp.
Pamiętaj, że w Swift 4, przy wyłączonym wnioskowaniu objc, musisz dodać @objcprzed funkcją wywołania zwrotnego dla perform(...)opcji. Tak jak:@objc func callback() {
leanne
32
Zgadzam się z Palle, że używanie dispatch_afterjest tutaj dobrym wyborem . Ale prawdopodobnie nie lubisz połączeń GCD, ponieważ pisanie ich jest dość denerwujące . Zamiast tego możesz dodać tego poręcznego pomocnika :
Teraz po prostu opóźniasz kod w wątku w tle, takim jak ten:
delay(bySeconds:1.5, dispatchLevel:.background){// delayed code that will run on background thread
}
Opóźnianie kodu w głównym wątku jest jeszcze prostsze:
delay(bySeconds:1.5){// delayed code, by default run in main thread
}
Jeśli wolisz Framework, który ma również kilka przydatnych funkcji, sprawdź HandySwift . Możesz dodać go do swojego projektu za pośrednictwem Kartaginy lub Accio, a następnie użyć go dokładnie tak, jak w powyższych przykładach:
To wydaje się bardzo pomocne. Czy masz coś przeciwko aktualizacji w szybkiej wersji 3.0? Dzięki
grahamcracker1234
Zacząłem migrować HandySwift do Swift 3 i planuję wydać nową wersję w przyszłym tygodniu. Potem też zaktualizuję powyższy skrypt. Bądźcie czujni.
Jeehut,
Migracja HandySwift do Swift 3 została zakończona i wydana w wersji 1.3.0. Właśnie zaktualizowałem powyższy kod, aby działał z Swift 3! :)
Jeehut,
1
Dlaczego mnożymy przez NSEC_PER_SEC, a następnie dzielimy przez to ponownie? Czy to nie jest zbędne? (np. Double (Int64 (sekundy * Double (NSEC_PER_SEC))) / Double (NSEC_PER_SEC)) Czy nie możesz po prostu napisać „DispatchTime.now () + Double (sekundy)?
Mark A. Donohoe
1
Dziękuję @MarqueIV, oczywiście masz rację. Właśnie zaktualizowałem kod. Był to po prostu wynik automatycznej konwersji z Xcode 8 i konwersja typu była potrzebna wcześniej, ponieważ DispatchTimenie była dostępna w Swift 2. Tak było let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))wcześniej.
Jeehut
24
Możesz to również zrobić za pomocą Swift 3.
Wykonaj funkcję po takim opóźnieniu.
overridefunc viewDidLoad(){super.viewDidLoad()self.perform(#selector(ClassName.performAction), with:nil, afterDelay:2.0)}@objc func performAction(){//This function will perform after 2 seconds
print("Delayed")}
Masz 4 sposoby na opóźnienie. Spośród tych opcji 1 lepiej jest wywołać lub wykonać funkcję po pewnym czasie. Funkcja sleep () jest najmniej używana.
Opcja 1.
DispatchQueue.main.asyncAfter(deadline:.now()+5.0){self.yourFuncHere()}//Your function here
func yourFuncHere(){}
Opcja 2.
perform(#selector(yourFuncHere2), with:nil, afterDelay:5.0)//Your function here
@objc func yourFuncHere2(){
print("this is...")}
Opcja 3.
Timer.scheduledTimer(timeInterval:5.0, target:self, selector:#selector(yourFuncHere3), userInfo:nil, repeats:false)//Your function here
@objc func yourFuncHere3(){}
Opcja 4.
sleep(5)
Jeśli chcesz wywołać funkcję po pewnym czasie, aby coś wykonać, nie używaj trybu uśpienia .
Odpowiedź @nneonneo sugeruje użycie, NSTimerale nie pokazuje, jak to zrobić. To jest podstawowa składnia:
let delay =0.5// time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target:self, selector:#selector(myFunctionName), userInfo:nil, repeats:false)
Oto bardzo prosty projekt pokazujący, jak można go użyć. Po naciśnięciu przycisku uruchamia się timer, który wywoła funkcję po upływie pół sekundy.
importUIKitclassViewController:UIViewController{var timer =NSTimer()let delay =0.5// start timer when button is tapped
@IBActionfunc startTimerButtonTapped(sender:UIButton){// cancel the timer in case the button is tapped multiple times
timer.invalidate()// start the timer
timer =NSTimer.scheduledTimerWithTimeInterval(delay, target:self, selector:#selector(delayedAction), userInfo:nil, repeats:false)}// function to be called after the delay
func delayedAction(){
print("action has started")}}
Używanie dispatch_time(jak w odpowiedzi Palle ) to kolejna ważna opcja. Jednak trudno jest anulować . Za pomocą NSTimer, aby anulować opóźnione zdarzenie, zanim ono nastąpi, wystarczy zadzwonić
timer.invalidate()
Używanie sleepnie jest zalecane, szczególnie w głównym wątku, ponieważ zatrzymuje on całą pracę nad wątkiem.
DispatchQueue.global(qos:.background).async {
sleep(4)
print("Active after 4 sec, and doesn't block main")DispatchQueue.main.async{//do stuff in the main thread here
}}
DispatchQueue.global(qos:.userInitiated).async {// Code is running in a background thread already so it is safe to sleep
Thread.sleep(forTimeInterval:4.0)}
(Zobacz inne odpowiedzi na sugestie, gdy Twój kod działa w głównym wątku).
Aby utworzyć proste opóźnienie czasowe, możesz zaimportować Darwin, a następnie użyć uśpienia (sekund), aby zrobić opóźnienie. Zajmuje to jednak całe sekundy, więc aby uzyskać bardziej precyzyjne pomiary, możesz zaimportować Darwina i użyć usle (milionowych części sekundy) do bardzo precyzyjnych pomiarów. Aby to przetestować, napisałem:
importDarwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Które wydruki czekają 1 sekundę i drukują, następnie czekają 0,4 sekundy, a następnie drukują. Wszystko działało zgodnie z oczekiwaniami.
Odpowiedzi:
Zamiast trybu uśpienia, który zablokuje Twój program, jeśli zostanie wywołany z wątku interfejsu użytkownika, rozważ użycie
NSTimer
timera wysyłania.Ale jeśli naprawdę potrzebujesz opóźnienia w bieżącym wątku:
Korzysta z
sleep
funkcji UNIX.źródło
Użycie
dispatch_after
bloku jest w większości przypadków lepsze niż użycie,sleep(time)
ponieważ wątek, w którym wykonywany jest sen, jest blokowany przed wykonywaniem innej pracy. podczas korzystaniadispatch_after
z wątku, który jest przetwarzany, nie jest blokowany, dzięki czemu może wykonywać inne prace w międzyczasie.Jeśli pracujesz nad głównym wątkiem aplikacji, korzystanie z niego
sleep(time)
jest niekorzystne dla wygody użytkownika aplikacji, ponieważ interfejs użytkownika nie odpowiada w tym czasie.Wyślij po zaplanowaniu wykonania bloku kodu zamiast zamrażania wątku:
Szybki ≥ 3,0
Szybki <3,0
źródło
.now() + .seconds(4)
podaje błąd:expression type is ambiguous without more context
.now() + .seconds(5)
że jest po prostu.now() + 5
Porównanie różnych podejść w swift 3.0
1. Spać
Ta metoda nie ma oddzwonienia. Umieść kody bezpośrednio po tym wierszu, aby wykonać je w 4 sekundy. Powstrzyma użytkownika od iteracji z elementami interfejsu użytkownika, takimi jak przycisk testowy, aż do upływu czasu. Chociaż przycisk jest jakby zamrożony, gdy zaczyna się sen, inne elementy, takie jak wskaźnik aktywności, wciąż się obracają bez zamrażania. Nie możesz ponownie uruchomić tej akcji podczas snu.
2. Wyślij, wykonaj i stoper
Te trzy metody działają podobnie, wszystkie działają w wątku w tle z oddzwanianiem, tylko z inną składnią i nieco innymi funkcjami.
Polecenie Dispatch jest powszechnie używane do uruchamiania czegoś w wątku w tle. Ma funkcję zwrotną jako część wywołania funkcji
Perform jest tak naprawdę uproszczonym timerem. Ustawia timer z opóźnieniem, a następnie uruchamia funkcję selektorem.
I wreszcie, timer zapewnia również możliwość powtarzania oddzwaniania, co w tym przypadku nie jest przydatne
W przypadku wszystkich tych trzech metod kliknięcie przycisku w celu ich uruchomienia powoduje, że interfejs użytkownika nie zawiesza się i można kliknąć go ponownie. Ponowne kliknięcie przycisku powoduje ustawienie innego timera i oddzwanianie zostanie uruchomione dwukrotnie.
Podsumowując
Żadna z czterech metod nie działa wystarczająco dobrze sama z siebie.
sleep
wyłączy interakcję użytkownika, więc ekran „ zawiesza się ” (nie w rzeczywistości) i powoduje złe wrażenia użytkownika. Pozostałe trzy metody nie zawieszają ekranu, ale możesz je uruchomić wiele razy i przez większość czasu chcesz poczekać, aż odzyskasz połączenie, zanim zezwolisz użytkownikowi na ponowne nawiązanie połączenia.Tak więc lepszym projektem będzie użycie jednej z trzech metod asynchronicznych z blokowaniem ekranu. Gdy użytkownik kliknie przycisk, zakryj cały ekran półprzezroczystym widokiem ze wskaźnikiem aktywności wirowania u góry, informującym użytkownika, że kliknięcie przycisku jest obsługiwane. Następnie usuń widok i wskaźnik w funkcji oddzwaniania, informując użytkownika, że akcja jest odpowiednio obsługiwana itp.
źródło
@objc
przed funkcją wywołania zwrotnego dlaperform(...)
opcji. Tak jak:@objc func callback() {
Zgadzam się z Palle, że używanie
dispatch_after
jest tutaj dobrym wyborem . Ale prawdopodobnie nie lubisz połączeń GCD, ponieważ pisanie ich jest dość denerwujące . Zamiast tego możesz dodać tego poręcznego pomocnika :Teraz po prostu opóźniasz kod w wątku w tle, takim jak ten:
Opóźnianie kodu w głównym wątku jest jeszcze prostsze:
Jeśli wolisz Framework, który ma również kilka przydatnych funkcji, sprawdź HandySwift . Możesz dodać go do swojego projektu za pośrednictwem Kartaginy lub Accio, a następnie użyć go dokładnie tak, jak w powyższych przykładach:
źródło
DispatchTime
nie była dostępna w Swift 2. Tak byłolet dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
wcześniej.Możesz to również zrobić za pomocą Swift 3.
Wykonaj funkcję po takim opóźnieniu.
źródło
W Swift 4.2 i Xcode 10.1
Masz 4 sposoby na opóźnienie. Spośród tych opcji 1 lepiej jest wywołać lub wykonać funkcję po pewnym czasie. Funkcja sleep () jest najmniej używana.
Opcja 1.
Opcja 2.
Opcja 3.
Opcja 4.
Jeśli chcesz wywołać funkcję po pewnym czasie, aby coś wykonać, nie używaj trybu uśpienia .
źródło
NSTimer
Odpowiedź @nneonneo sugeruje użycie,
NSTimer
ale nie pokazuje, jak to zrobić. To jest podstawowa składnia:Oto bardzo prosty projekt pokazujący, jak można go użyć. Po naciśnięciu przycisku uruchamia się timer, który wywoła funkcję po upływie pół sekundy.
Używanie
dispatch_time
(jak w odpowiedzi Palle ) to kolejna ważna opcja. Jednak trudno jest anulować . Za pomocąNSTimer
, aby anulować opóźnione zdarzenie, zanim ono nastąpi, wystarczy zadzwonićUżywanie
sleep
nie jest zalecane, szczególnie w głównym wątku, ponieważ zatrzymuje on całą pracę nad wątkiem.Patrz tutaj dla mojego pełniejszą odpowiedź.
źródło
Wypróbuj następującą implementację w Swift 3.0
Stosowanie
źródło
Możesz utworzyć rozszerzenie, aby łatwo korzystać z funkcji opóźnienia (Składnia: Swift 4.2+)
Jak korzystać z UIViewController
źródło
Jeśli konieczne jest ustawienie opóźnienia krótszego niż sekunda, nie trzeba ustawiać parametru .seconds. Mam nadzieję, że to komuś się przyda.
źródło
źródło
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}
jest prawdopodobnie bardziej poprawne ✌️Jeśli kod działa już w wątku w tle, wstrzymaj wątek, używając tej metody w programie Foundation :
Thread.sleep(forTimeInterval:)
Na przykład:
(Zobacz inne odpowiedzi na sugestie, gdy Twój kod działa w głównym wątku).
źródło
Aby utworzyć proste opóźnienie czasowe, możesz zaimportować Darwin, a następnie użyć uśpienia (sekund), aby zrobić opóźnienie. Zajmuje to jednak całe sekundy, więc aby uzyskać bardziej precyzyjne pomiary, możesz zaimportować Darwina i użyć usle (milionowych części sekundy) do bardzo precyzyjnych pomiarów. Aby to przetestować, napisałem:
Które wydruki czekają 1 sekundę i drukują, następnie czekają 0,4 sekundy, a następnie drukują. Wszystko działało zgodnie z oczekiwaniami.
źródło
to jest najprostsze
źródło