Swift - scheduledTimerWithTimeInterval - NSInvocation

问题描述:

我想在未来安排函数调用。我正在使用Swift。Swift - scheduledTimerWithTimeInterval - NSInvocation

我要回调是私有的,返回一个承诺(从PromiseKit)的方法

我见过使用

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

精细的所有例子。我试过了

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

这个失败与No method declared with Objective-C selector 'connect'

Objective-C在这里做什么?

无论如何建议我在我的方法connect前添加@objc。精细。嗯,我不能因为显然Method cannot be marked @objc because its result type cannot be represented in Objective-C

如果我想用客观-C我不会写斯威夫特...

还有一个scheduledTimerWithTimeInterval

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

但是,从我读过NSInvocation不是斯威夫特的事情...

所以我结束了创建一个包装,什么也不做其他比调用connect并返回Void这一目标C能understan d。它有效,但感觉非常愚蠢。有更好的Swift方法吗?

奖励:为什么JavaScript可以这么做,就像setTimeout(this.connect, 1)一样,Swift没有内置的方法可以找到?

与iOS 10和夫特3开始,它是能够使用(NS)定时器与块闭合,从而避免Objective-C中选择的调用当计时器触发:

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

除了避免@objc装饰,使用这种技术允许你调用方法,其包括非目标C兼容的参数类型,例如枚举和自选。

回复:setTimeout(this.connect, 1)从JavaScript,如果你不需要将其取消,斯威夫特3更直接的类比可能是:

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

这是相当的接近因为你确实有一个选择其中的线程运行;-)

+0

非常好,谢谢! – Guig

记住雨燕2.2 /的Xcode 7.3已使用选择的新方法:Selector("funcName")改为#selector(ClassName.funcName)

你应该使用#selector

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

Selector("connect"),但请记住,你会收到一个警告:

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

也看看this为了知道如何使用Selector()

更多信息引用一个方法 here的Objective-C的选择

+0

是的两个都需要选择器是可以理解的Objective-C,这不是我的情况,因为它是返回一个Promise(PromiseKit) – Guig

+0

@Guig下次可能提供更多的代码? – fuzz

+0

:)当然。我上面提到它:“我不能,因为显然'方法不能被标记@objc,因为它的结果类型不能用Objective-C代表”,但我同意代码更易于阅读 – Guig

有2种方式,你可以调用一个计时器:

// 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) 

而且两者假设你有这样的功能:

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

什么是Objective-C的在这里做什么?

需要Objective-C的原因是它在运行时动态地绑定了“调用”(它在Objective-C中没有调用),而Swift不能这样做。 Swift无法在定时器类中拥有“调用”在编译时NSTimer处未知的“函数”的代码。

作者:NSTimer使用NSInvocation(或类似unswifty技术)做“呼叫”。因此,NSTimer的使用不会更快速,但对后期绑定的需求更加混淆,以使Swift开发人员感觉更好。

如果我想用客观-C我不会写斯威夫特...

即使你的代码是完全用斯威夫特,它需要从Objective-C的后期绑定群众利益。 Cocoa的许多核心技术都不可能在Swift中编写,包括响应者链,撤消管理器,核心数据,动画......(另一方面,您可以定义一个操作符,软件工程中的一个重大进展,并描述整个故事)。

即使使用swift,NSTimer的回调也需要Objective-C的访问。在我的情况下,这意味着两件事:

  • 修饰私有方法@objc,因为默认私有方法不能从Objective-C从Swift类访问。
  • 将我的方法包装在调用它的方法中,并且不返回任何内容,以便回调函数返回void。这是必要的,因为Objective-C不知道如何处理Promise类型。

所以最终它看​​起来像:

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) 
    } 
}