dispatch_after - GCD in Swift?


558

Ich habe das iBook von Apple durchgesehen und konnte keine Definition dafür finden:

Kann jemand die Struktur von erklären dispatch_after?

dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)

1
Apple hat dieses Buch 2018 nicht veröffentlicht. Das neueste Archiv, das ich finden konnte, stammt aus dem Dezember 2017 . Alte Links zum iBook leiten jetzt einfach zu developer.apple.com/documentation/swift weiter .
Cœur

Antworten:


742

Eine klarere Vorstellung von der Struktur:

dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)

dispatch_time_tist ein UInt64. Der dispatch_queue_tTyp ist tatsächlich mit einem Alias ​​versehen NSObject, aber Sie sollten nur Ihre bekannten GCD-Methoden verwenden, um Warteschlangen abzurufen . Der Block ist ein Swift-Verschluss. Insbesondere dispatch_block_tist definiert als () -> Void, was äquivalent zu ist () -> ().

Anwendungsbeispiel:

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    print("test")
}

BEARBEITEN:

Ich empfehle die wirklich schöne delayFunktion von @ matt .

EDIT 2:

In Swift 3 wird es neue Wrapper für GCD geben. Siehe hier: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

Das ursprüngliche Beispiel würde in Swift 3 wie folgt geschrieben:

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
    print("test")
}

Beachten Sie, dass Sie die deadlineTimeDeklaration als schreiben DispatchTime.now() + 1.0und das gleiche Ergebnis erhalten können, da der +Operator wie folgt überschrieben wird (ähnlich für -):

  • func +(time: DispatchTime, seconds: Double) -> DispatchTime
  • func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime

Dies bedeutet, dass, wenn Sie das nicht verwenden DispatchTimeInterval enumund nur eine Zahl schreiben, davon ausgegangen wird, dass Sie Sekunden verwenden.


17
Tipp: Da der Block der letzte Parameter der Funktion ist, können Sie Swifts Syntax "Trailing Closure" für zusätzliche Lesbarkeit verwenden:dispatch_after(1, dispatch_get_main_queue()) { println("test") }
Bill

8
Ich denke, die Verwendung der Nummer 1in dispatch_after(1, ...kann hier viel Verwirrung stiften. Die Leute werden denken, dass es eine Anzahl von Sekunden ist, wenn es tatsächlich eine Nanosekunde ist . Ich schlage vor, die Antwort von @brindy zu lesen, wie diese Nummer richtig erstellt wird.
Hlung

3
Bitte wechseln Sie 1zu, dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))da dies zu Verwirrung führt. Die Leute könnten denken, dass Sie in Swift
OemerA

4
Die Swift 3-Version scheint nicht zu funktionieren. Es beschwert sich, dass Binary operator '+' cannot be applied to operands of type DispatchTime and '_'auf der Linielet delayTime = DispatchTime.now() + .seconds(1.0)
Andy Ibanez

9
Das Umschreiben DispatchTime.now() + 1.0scheint der einzige Weg zu sein, damit es funktioniert (keine Notwendigkeit .seconds)
Andy Ibanez

1092

Ich benutze dispatch_afterso oft, dass ich eine Dienstprogrammfunktion der obersten Ebene geschrieben habe, um die Syntax zu vereinfachen:

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Und jetzt können Sie so sprechen:

delay(0.4) {
    // do stuff
}

Wow, eine Sprache, in der Sie die Sprache verbessern können. Was wäre besser?


Update für Swift 3, Xcode 8 Seed 6

Es scheint fast nicht wert zu sein, sich damit zu beschäftigen, nachdem sie die aufrufende Syntax verbessert haben:

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

2
Ich brauchte nur eine Verknüpfung für die Verzögerungsberechnung und endete mit:func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }
Aviel Gross

4
@ agf119105 Wenn Sie nur eine Codezeile im Abschluss haben, fügen Sie eine weitere Codezeile hinzu (z return. B. ).
Matt

2
@GastonM Irrelevant. Das Übergeben einer Funktion hat an sich keine Probleme mit der Speicherverwaltung.
Matt

7
"Eine Sprache, in der Sie die Sprache verbessern können". Ich verstehe nicht, wie das Definieren einer globalen Funktion die Sprache verbessert oder warum dies in C nicht möglich ist. Vielleicht, wenn Sie einen Operator überladen;)1.0 ~~ { code...}
Yerk

8
Sie stellen die Richtigkeit Ihrer Antwort nicht in Frage - aber ist "Ich benutze dispatch_after so oft" kein Codegeruch, der am besten bekämpft werden kann, wenn Sie keine Komfortfunktion bereitstellen?
Nikolai Ruhe

128

Swift 3+

Dies ist in Swift 3+ super einfach und elegant:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
    // ...
}

Ältere Antwort:

Um die Antwort von Cezary zu erweitern, die nach 1 Nanosekunde ausgeführt wird, musste ich Folgendes tun, um nach 4 ½ Sekunden auszuführen.

let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)

Bearbeiten: Ich habe festgestellt, dass mein ursprünglicher Code leicht falsch war. Die implizite Eingabe verursacht einen Kompilierungsfehler, wenn Sie NSEC_PER_SEC nicht in ein Double umwandeln.

Wenn jemand eine optimalere Lösung vorschlagen kann, würde ich sie gerne hören.


Ich erhalte einen Compilerfehler für eine veraltete API mit dispatch_get_current_queue(). Ich habe dispatch_get_main_queue()stattdessen verwendet.
David L

@ DavidL - danke, dispatch_get_main_queue()ist definitiv das, was Sie verwenden sollten. Werde Dich auf dem Laufenden halten.
Brindy

Ich habe dies auf einem Spielplatz mit Swift 3 versucht und es funktioniert nicht
μολὼν.λαβέ

@GAlexander Funktioniert für mich. Erlauben Sie dem Spielplatz, auf unbestimmte Zeit ausgeführt zu werden?
Brindy

Ähm, na nein, ich habe ein paar Stunden laufen lassen und immer noch nichts gedruckt. Hier ist was ich verwendet habe. "importieren Versand importieren Darwin importieren CoreGraphics 'DispatchQueue.main.asyncAfter (Frist: .now () + 4.5) {print (" got here ")}"
μολὼν.λαβέ

83

Die Syntax von matt ist sehr gut und wenn Sie den Block ungültig machen müssen, können Sie Folgendes verwenden:

typealias dispatch_cancelable_closure = (cancel : Bool) -> Void

func delay(time:NSTimeInterval, closure:()->Void) ->  dispatch_cancelable_closure? {

    func dispatch_later(clsr:()->Void) {
        dispatch_after(
            dispatch_time(
                DISPATCH_TIME_NOW,
                Int64(time * Double(NSEC_PER_SEC))
            ),
            dispatch_get_main_queue(), clsr)
    }

    var closure:dispatch_block_t? = closure
    var cancelableClosure:dispatch_cancelable_closure?

    let delayedClosure:dispatch_cancelable_closure = { cancel in
        if closure != nil {
            if (cancel == false) {
                dispatch_async(dispatch_get_main_queue(), closure!);
            }
        }
        closure = nil
        cancelableClosure = nil
    }

    cancelableClosure = delayedClosure

    dispatch_later {
        if let delayedClosure = cancelableClosure {
            delayedClosure(cancel: false)
        }
    }

    return cancelableClosure;
}

func cancel_delay(closure:dispatch_cancelable_closure?) {

    if closure != nil {
        closure!(cancel: true)
    }
}

Verwenden Sie wie folgt

let retVal = delay(2.0) {
    println("Later")
}
delay(1.0) {
    cancel_delay(retVal)
}

Credits

Link oben scheint unten zu sein. Original Objc Code von Github


1
Das einzige Leistungsmerkmal von performSelector: afterDelay ist die Möglichkeit, es abzubrechen. Nur diese Lösung deckt das Problem ab. Vielen Dank
HotJard

@HotJard Beachten Sie, dass dies performSelector:afterDelay:jetzt in Swift 2 verfügbar ist, sodass Sie es abbrechen können.
Matt

@matt, aber es ist nur für NSObject verfügbar, nicht wahr?
HotJard

@HotJard Sicher, aber das ist besser, als es überhaupt nicht zu haben. Ich sehe dort kein Problem. Genau wie bei dieser Antwort hatte ich den Verlust jedoch bereits durch das Schreiben eines GCD-basierten stornierbaren Timers kompensiert (mit a dispatch_source_t, da dies etwas ist, das Sie stornieren können).
Matt

2
Vielen Dank, ich habe dies bis zu Swift 2.3 verwendet. Swift 3.0 Compiler beschwert sich jetzt, wäre toll, wenn Sie Ihre Antwort aktualisieren würden!
Nichtautomatischer

27

Einfachste Lösung in Swift 3.0 & Swift 4.0 & Swift 5.0

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { 
        completion()
    }
}

Verwendungszweck

delayWithSeconds(1) {
   //Do something
}

22

Apple hat ein dispatch_after-Snippet für Objective-C :

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    <#code to be executed after a specified delay#>
});

Hier ist das gleiche Snippet, das auf Swift 3 portiert wurde :

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + <#delayInSeconds#>) {
  <#code to be executed after a specified delay#>
}

14

Eine andere Möglichkeit besteht darin, Double folgendermaßen zu erweitern:

extension Double {
   var dispatchTime: dispatch_time_t {
       get {
           return dispatch_time(DISPATCH_TIME_NOW,Int64(self * Double(NSEC_PER_SEC)))
       }
   }
}

Dann können Sie es so verwenden:

dispatch_after(Double(2.0).dispatchTime, dispatch_get_main_queue(), { () -> Void in
            self.dismissViewControllerAnimated(true, completion: nil)
    })

Ich mag die Verzögerungsfunktion von Matt, aber aus Präferenz möchte ich lieber die Weitergabe von Schließungen einschränken.


8

In Swift 3.0

Versandwarteschlangen

  DispatchQueue(label: "test").async {
        //long running Background Task
        for obj in 0...1000 {
            print("async \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.async(execute: { 
            print("UI update on main queue")
        })

    }

    DispatchQueue(label: "m").sync {
        //long running Background Task
        for obj in 0...1000 {
            print("sync \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.sync(execute: {
            print("UI update on main queue")
        })
    }

Versand nach 5 Sekunden

    DispatchQueue.main.after(when: DispatchTime.now() + 5) {
        print("Dispatch after 5 sec")
    }

4

Swift 3.0 Version

Führen Sie nach der Schließfunktion eine Aufgabe nach der Verzögerung im Hauptthread aus.

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Nennen Sie diese Funktion wie folgt:

performAfterDelay(delay: 4.0) {
  print("test")
}

4

1) Fügen Sie diese Methode als Teil der UIViewController-Erweiterung hinzu.

extension UIViewController{
func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(time, dispatch_get_main_queue(), block)
    }
}

Rufen Sie diese Methode in VC auf:

    self.runAfterDelay(5.0, block: {
     //Add code to this block
        print("run After Delay Success")
    })

2)

performSelector("yourMethod Name", withObject: nil, afterDelay: 1)

3)

override func viewWillAppear(animated: Bool) {

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue(), { () -> () in
    //Code Here
})

// Kompaktes Formular

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) {
    //Code here
 }
}

3

Obwohl dies nicht die ursprüngliche Frage des OP ist, wurden bestimmte NSTimerverwandte Fragen als Duplikate dieser Frage markiert. Es lohnt sich daher NSTimer, hier eine Antwort aufzunehmen.

NSTimer vs. dispatch_after

  • NSTimerist höher, während dispatch_afterniedriger ist.
  • NSTimerist einfacher abzubrechen. Zum Abbrechen dispatch_aftermuss mehr Code geschrieben werden .

Eine Aufgabe verzögern mit NSTimer

Erstellen Sie eine NSTimerInstanz.

var timer = NSTimer()

Starten Sie den Timer mit der Verzögerung, die Sie benötigen.

// invalidate the timer if there is any chance that it could have been called before
timer.invalidate()
// delay of 2 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 

Fügen Sie eine Funktion hinzu, die nach der Verzögerung aufgerufen werden soll (unter Verwendung des Namens, den Sie für den selectorobigen Parameter verwendet haben).

func delayedAction() {
    print("Delayed action has now started."
}

Anmerkungen

  • Wenn Sie die Aktion abbrechen müssen, bevor sie ausgeführt wird, rufen Sie einfach an timer.invalidate().
  • Für eine wiederholte Aktion verwenden repeats: true.
  • Wenn Sie ein einmaliges Ereignis haben, das nicht abgebrochen werden muss, muss die timerInstanzvariable nicht erstellt werden. Folgendes wird ausreichen:

    NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 
  • Siehe meine ausführlichere Antwort hier .


3

Verwenden Sie dies für mehrere Funktionen. Dies ist sehr hilfreich, um Animationen oder Activity Loader für statische Funktionen oder UI-Updates zu verwenden.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
            // Call your function 1
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                // Call your function 2
            }
        }

Beispiel: Verwenden Sie eine Animation, bevor eine tableView neu geladen wird. Oder ein anderes UI-Update nach der Animation.

*// Start your amination* 
self.startAnimation()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
                *// The animation will execute depending on the delay time*
                self.stopAnimation()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    *// Now update your view*
                     self.fetchData()
                     self.updateUI()
                }
            }

2

Das hat bei mir funktioniert.

Swift 3:

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

Ziel c:

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});

2

Swift 3 & 4:

Sie können eine Erweiterung für DispatchQueue erstellen und eine Funktionsverzögerung hinzufügen, die die Funktion DispatchQueue asyncAfter intern verwendet

extension DispatchQueue {
    static func delay(_ delay: DispatchTimeInterval, closure: @escaping () -> ()) {
        let timeInterval = DispatchTime.now() + delay
        DispatchQueue.main.asyncAfter(deadline: timeInterval, execute: closure)
    }
}

verwenden:

DispatchQueue.delay(.seconds(1)) {
    print("This is after delay")
}

1

Ein weiterer Helfer zum Verzögern Ihres Codes, der zu 100% schnell verwendet wird und optional die Auswahl eines anderen Threads ermöglicht , aus dem Sie Ihren verzögerten Code ausführen können:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

Jetzt verzögern Sie einfach Ihren Code im Haupt-Thread wie folgt:

delay(bySeconds: 1.5) { 
    // delayed code
}

Wenn Sie Ihren Code auf einen anderen Thread verschieben möchten :

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

Wenn Sie ein Framework bevorzugen , das auch einige weitere praktische Funktionen bietet, lesen Sie HandySwift . Sie können es über Karthago zu Ihrem Projekt hinzufügen und dann genau wie in den obigen Beispielen verwenden, z.

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}

1

Ich bevorzuge immer die Erweiterung anstelle von freien Funktionen.

Swift 4

public extension DispatchQueue {

  private class func delay(delay: TimeInterval, closure: @escaping () -> Void) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
  }

  class func performAction(after seconds: TimeInterval, callBack: @escaping (() -> Void) ) {
    DispatchQueue.delay(delay: seconds) {
      callBack()
    }
  }

}

Verwenden Sie wie folgt.

DispatchQueue.performAction(after: 0.3) {
  // Code Here
}

1

Verzögern des GCD-Aufrufs mit asyncAfter in Kürze

let delayQueue = DispatchQueue(label: "com.theappmaker.in", qos: .userInitiated)
let additionalTime: DispatchTimeInterval = .seconds(2)

Wir können als ** Mikrosekunden , Millisekunden , Nanosekunden verzögern

delayQueue.asyncAfter(deadline: .now() + 0.60) {
    print(Date())
}

delayQueue.asyncAfter(deadline: .now() + additionalTime) {
    print(Date())
}

1

In Swift 4

Verwenden Sie dieses Snippet:

    let delayInSec = 1.0
    DispatchQueue.main.asyncAfter(deadline: .now() + delayInSec) {
       // code here
       print("It works")
    }

Dies ist bereits in anderen Antworten (zum Beispiel Brindy oder Rahul) ... gleiche Syntax ...
Eric Aya

1
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...
});

Die dispatch_after(_:_:_:)Funktion akzeptiert drei Parameter:

eine Verzögerung
einer Versandwarteschlange
eines Blocks oder einer Schließung

Die dispatch_after(_:_:_:)Funktion ruft den Block oder Abschluss in der Versandwarteschlange auf, der nach einer bestimmten Verzögerung an die Funktion übergeben wird. Beachten Sie, dass die Verzögerung mit der dispatch_time(_:_:)Funktion erstellt wird. Denken Sie daran, da wir diese Funktion auch in Swift verwenden.

Ich empfehle das Tutorial Raywenderlich Dispatch Tutorial durchzugehen


1

Verwenden Sie in Swift 5 Folgendes:

 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: closure) 

// time gap, specify unit is second
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
            Singleton.shared().printDate()
        }
// default time gap is second, you can reduce it
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
          // just do it!
    }

0

Verwenden Sie diesen Code, um nach 2,0 Sekunden eine UI-bezogene Aufgabe auszuführen.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })

Swift 3.0 Version

Führen Sie nach der Schließfunktion eine Aufgabe nach der Verzögerung im Hauptthread aus.

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Nennen Sie diese Funktion wie folgt:

performAfterDelay(delay: 4.0) {
  print("test")
}

1
Dies ist fast identisch mit den vorherigen Antworten
Daniel Galasko

Es scheint, dass diese Antwort Anfang 2016 gemacht wurde und älter ist als mindestens andere 6 Antworten ..
eharo2

0

Jetzt mehr als syntaktischer Zucker für asynchrone Versendungen in Grand Central Dispatch (GCD) in Swift.

Podfile hinzufügen

pod 'AsyncSwift'

Dann können Sie es so verwenden.

let seconds = 3.0
Async.main(after: seconds) {
print("Is called after 3 seconds")
}.background(after: 6.0) {
print("At least 3.0 seconds after previous block, and 6.0 after Async code is called")
}

Apple hat uns alles gegeben, was wir brauchen, um GCD in wenigen Zeilen zu verwenden. Warum sich mit Pods, Arbeitsbereichen usw. beschäftigen? Lesen Sie einfach die Dokumente zu @escaping und Capturing. Es reicht.
Ingconti

0

Swift 4 hat eine ziemlich kurze Möglichkeit, dies zu tun:

Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
    // Your stuff here
    print("hello")
}

0

Hier ist die synchrone Version von asyncAfter in Swift:

let deadline = DispatchTime.now() + .seconds(3)
let semaphore = DispatchSemaphore.init(value: 0)
DispatchQueue.global().asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
    semaphore.signal()
}

semaphore.wait()

Zusammen mit asynchronen:

let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.