Swift - scheduledTimerWithTimeInterval - NSInvocation

时间:2016-07-05 02:45:58

标签: swift timer

我想在将来安排一个函数调用。我正在使用Swift。

我想回调一个私有的方法并返回一个Promise(来自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在这做什么?

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

如果我想使用Objective-C,我不会写Swift ......

还有另一个scheduledTimerWithTimeInterval

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

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

所以我最终创建了一个包装器,除了调用connect并返回Objective C可以理解的Void之外什么都不做。它有效,但感觉非常愚蠢。有更好的Swift方式吗?

额外奖励:为什么javascript可以像setTimeout(this.connect, 1)一样简单地执行此操作而Swift没有我可以找到的内置方式?

5 个答案:

答案 0 :(得分:4)

从iOS 10和Swift 3开始,可以使用带有块闭包的(NS)Timer,从而避免在计时器触发时调用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装饰器之外,使用此技术还可以调用包含非Objective-C兼容参数类型(如枚举和选项)的方法。

来自Javascript的Re:setTimeout(this.connect, 1),如果您不需要取消它,可以在Swift 3中进行更直接的类比:

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

考虑到你实际上可以选择运行哪个线程,这是非常接近的; - )

答案 1 :(得分:1)

  

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

需要Objective-C的原因是它动态地绑定了#34; call" (在Objective-C中没有调用)在运行时,而Swift无法做到这一点。 Swift无法在定时器类中使用"调用" a"功能"在NSTimer的编译时间不知道。

顺便说一句:NSTimer使用NSInvocation(或类似的无法使用的技术)进行"调用"。因此NSTimer的使用并不是更加明确,但对后期绑定的需求更加模糊,以使Swift开发人员感觉更好。

  

如果我想使用Objective-C我不会写Swift ......

即使您的代码完全是用Swift编写的,它也会从Objective-C的后期绑定中获得大量好处。 Cocoa的许多核心技术都无法在Swift中编写,包括响应链,撤消管理器,核心数据,动画......(另一方面,您可以定义一个运算符,在软件工程中取得了哪些重大进展并描述了整个故事。)

答案 2 :(得分:0)

请记住,Swift 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()

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

答案 3 :(得分:0)

有两种方法可以调用计时器:

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

答案 4 :(得分:0)

即使您正在使用swift,Objective-C也需要可以访问NSTimer的回调。就我而言,它意味着两件事:

  • 使用@objc修饰私有方法,因为默认情况下,Swift类的Objective-C无法访问私有方法。
  • 将my方法包装在一个调用它的方法中,并且不返回任何内容,以便回调返回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)
  }
}