【问题标题】:Swift - scheduledTimerWithTimeInterval - NSInvocationSwift - scheduleTimerWithTimeInterval - NSInvocation
【发布时间】:2016-07-05 02:45:58
【问题描述】:

我想安排将来的函数调用。我正在使用 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 在这里做什么?

无论如何建议我在我的方法connect 前面添加@objc。美好的。好吧,我不能,因为显然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 不是 Swift 的东西......

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

奖励:为什么 javascript 可以像 setTimeout(this.connect, 1) 那样简单地做到这一点,而 Swift 没有我能找到的内置方式?

【问题讨论】:

    标签: swift timer


    【解决方案1】:

    从 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 兼容参数类型的方法,例如枚举和可选项。

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

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

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

    【讨论】:

      【解决方案2】:

      Objective-C 在这里做什么?

      需要 Objective-C 的原因是它在运行时动态绑定了“调用”(在 Objective-C 中没有调用),而 Swift 无法做到这一点。 Swift 无法在计时器类中包含“调用”NSTimer 编译时未知的“函数”的代码。

      顺便说一句:NSTimer 使用NSInvocation(或类似的 unswifty 技术)进行“呼叫”。因此NSTimer 的使用并没有更快捷,但后期绑定的需求更加模糊,让 Swift 开发者感觉更好。

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

      即使您的代码完全是用 Swift 编写的,它也会从 Objective-C 的后期绑定中获益良多。 Cocoa 的许多核心技术是 Swift 无法编写的,包括响应链、撤消管理器、Core Data、动画......整个故事。)

      【讨论】:

        【解决方案3】:

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

        // 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】:

          请记住,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.

          【讨论】:

          • 是的,两者都需要选择器能够被 Objective-C 理解,这不是我的情况,因为它返回了一个 Promise(来自 PromiseKit)
          • :) 当然。我在上面提到过:“好吧,我不能,因为显然Method cannot be marked @objc because its result type cannot be represented in Objective-C”但我同意代码更容易阅读
          【解决方案5】:

          即使您使用的是 swift,Objective-C 也需要能够访问 NSTimer 的回调。就我而言,这意味着两件事:

          • 使用 @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)
            }
          }
          

          【讨论】:

            猜你喜欢
            • 2015-03-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-28
            相关资源
            最近更新 更多