2016-07-05 13 views
5

Chcę zaplanować wywołanie funkcji w przyszłości. Używam Swift.Swift - scheduleTimerWithTimeInterval - NSInvocation

Chcę callback metody, które są prywatne i zwraca obietnicę (od PromiseKit)

Wszystko przykładzie Widziałem Użyj

NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, target: AnyObject, selector: Selector, userInfo: AnyObject?, repeats: Bool) 

Fine. Podjęto próbę:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "connect", userInfo: nil, repeats: false) 

To nie powiedzie się z No method declared with Objective-C selector 'connect'.

Co robi Objective-C?

W każdym razie sugeruje się dodanie przed moją metodą @objcconnect. W porządku. No nie mogę, bo widocznie Method cannot be marked @objc because its result type cannot be represented in Objective-C

Gdybym chciał użyć objective-c Nie chciałbym pisać Swift ...

Jest inny scheduledTimerWithTimeInterval czyli

NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, invocation: NSInvocation, repeats: Bool) 

ale z tego co ja Wcześniej czytać NSInvocation Swift nie jest rzeczą ...

Więc skończyło się tworząc opakowanie, które robi nic innego niż wywołanie connect i powrocie Void że Objective C może understan re. Działa, ale wydaje się bardzo głupie. Czy jest lepszy sposób Swift?

Premia: dlaczego javascript może to zrobić po prostu jako setTimeout(this.connect, 1), a Swift nie ma wbudowanego w to, co mogę znaleźć?

Odpowiedz

4

Począwszy iOS 10 i Swift 3, to jest możliwe stosowanie (NS) Timer zamknięcia bloku, a tym samym uniknąć celu C selektor powołania się gdy pożary czasomierza:

if #available(iOS 10.0, *) { 
     Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { (Timer) in 
      self.connect() // per the OP's example 
     }) 
    } 

Oprócz unikania @objc dekorator, przy użyciu tej techniki pozwala wywoływać metody, które obejmują zakaz Objective-C-kompatybilne typy parametrów, takich jak teksty stałe i optionals.

Re: setTimeout(this.connect, 1) z Javascript, jeśli nie trzeba go anulować, bardziej bezpośrednia analogia w Swift 3 może być:

DispatchQueue.Main.asyncAfter(deadline: .now() + 1.0, execute { self.connect() }) 

Który jest cholernie blisko, zważywszy, że rzeczywiście mają do wyboru w tym wątku do uruchomienia ;-)

+0

Doskonale, dziękuję! – Guig

0

Pamiętaj Swift 2.2/Xcode 7.3 ma nowy sposób na wykorzystanie selektora: Selector("funcName") została zmieniona na #selector(ClassName.funcName)

Należy użyć #selector:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(YourClass.connect), userInfo: nil, repeats: false) 

LUB Selector("connect") jednak pamiętać, że ci „ll otrzymują ostrzeżenie:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("connect"), userInfo: nil, repeats: false) 

również spojrzeć this, aby wiedzieć, w jaki sposób użyj Selector().

Więcej informacji Odwoływanie się do selektora Cel-C metody here.

+0

Tak, oba wymagają, aby selektor był zrozumiały dla Celu C, co nie było moim przypadkiem, ponieważ zwracał Obietnicę (z PromiseKit). – Guig

+0

@Guig Być może następnym razem dostarczysz więcej kodu? – fuzz

+0

:) Pewnie. Wspomniałem jednak wyżej: "Nie mogę, ponieważ najwyraźniej" Metoda nie może być oznaczona jako @objc, ponieważ jej typ wyniku nie może być reprezentowany w Objective-C "", ale zgadzam się, że kod jest łatwiejszy do odczytania. – Guig

0

Istnieją 2 sposoby można wywołać Timer:

// Specify the selector name as String 
// Swift will give you a warning but this is handy if you have the function's name stored 
// as a variable or need some dynamism 
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timerFired"), userInfo: nil, repeats: false) 

// The recommended way since Swift 2.2/2.3 (can't remeber exactly) 
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(MyClass.timerFired), userInfo: nil, repeats: false) 

i oba zakłada masz funkcję tak:

func timerFired() { 
    print("Hello world") 
} 
0

Co Objective-C robi tutaj ??

Powodem potrzeby Objective-C jest dynamiczne wiązanie "wywołania" (nie jest to wywołanie w Objective-C) w czasie wykonywania, podczas gdy Swift nie może tego zrobić. Swift nie może mieć kodu w klasie zegara, który "wywołuje" "funkcję" nieznaną w czasie kompilacji NSTimer.

BTW: NSTimer używa NSInvocation (lub czegoś podobnego, niewiarygodnej technologii) do wykonania "połączenia". Dlatego użycie NSTimer nie jest bardziej szybkie, ale potrzeba późnego wiązania jest bardziej zaciemniona, aby programiści Swift czuli się lepiej.

Gdybym chciał użyć objective-c Nie chciałbym pisać Swift ...

Nawet kod jest napisany całkowicie w Swift, trwa mas świadczeń z Objective-C na późne wiązanie . Wiele podstawowych technik kakao jest niemożliwych do napisania w Swift, w tym łańcuch odpowiedzi, menedżer cofania, podstawowe dane, animacje ... (Z drugiej strony jesteś w stanie zdefiniować operatora, jaki jest duży postęp w inżynierii oprogramowania i opisuje cała historia.)

0

Oddzwonienie do NSTimer musi być dostępne dla Objective-C, nawet jeśli używasz szybkiego. W moim przypadku oznaczało to dwie rzeczy:

  • dekoracji prywatnej metody z @objc ponieważ domyślnie metody prywatne nie są dostępne dla Objective-C z klasami Swift.
  • Zawiń moją metodę metodą, która ją wywołuje i niczego nie zwraca, tak że wywołanie zwrotne zwraca pustkę. Było to konieczne, ponieważ Objective-C nie wiedział, jak obsługiwać typ Promise.

Tak na końcu wyglądało następująco:

import PromiseKit 

class bla : NSObject { 
    private func myCallback() -> Promise<Void> { 
    return Promise { fullfill, reject in 
     // ... 
    } 
    } 

    @objc private func myCallbackWrap -> Void { 
    myCallback() 
    } 

    func startTimeout() -> Void { 
     NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: #selector(myCallbackWrap), userInfo: nil, repeats: false) 
    } 
}