3

我正在尝试Timer在 Swift 3 中扩展 Foundation 的类,添加一个便利的初始化程序。但它对 Foundation 提供的初始化程序的调用永远不会返回。

下面的简单演示说明了该问题,该演示可以作为 Playground 运行。

import Foundation

extension Timer {
    convenience init(target: Any) {
        print("Next statement never returns")
        self.init(timeInterval: 1.0,
                  target: target,
                  selector: #selector(Target.fire),
                  userInfo: nil,
                  repeats: true)
        print("This never executes")
    }
}

class Target {
    @objc func fire(_ timer: Timer) {
    }
}

let target = Target()
let timer = Timer(target: target)

控制台输出:

下一条语句永远不会返回

为了进一步学习,

• 我编写了类似的扩展代码URLProtocol(仅有的具有实例初始化程序的其他 Foundation 类之一)。结果:没问题。

• 为了消除可能导致Objective-C 的东西,我将包装的初始化程序更改为init(timeInterval:repeats:block:)方法并提供了一个Swift 闭包。结果:同样的问题。

4

3 回答 3

4

我实际上并不知道答案,但我通过在带有调试器的实际应用程序中运行它看到存在无限递归(因此挂起)。我怀疑这是因为您实际上并没有调用 Timer 的指定初始化程序。这个事实并不明显,但是如果您尝试将 Timer 子类super.init(timeInterval...)化并调用编译器会抱怨,并且标题中还有一个奇怪的“未继承”标记super.init(timeInterval...)

我可以通过调用来解决这个问题self.init(fireAt:...)

extension Timer {
    convenience init(target: Any) {
        print("Next statement never returns") // but it does
        self.init(fireAt: Date(), interval: 1, target: target, 
            selector: #selector(Target.fire), userInfo: nil, repeats: true)
        print("This never executes") // but it does
    }
}

让它成为你想要的......

于 2016-12-24T22:18:26.407 回答
2

我看到了与 matt 描述的无限递归相同的问题。-[NSCFTimer release]在同一个对象上一遍又一遍地调用。通过从实例初始化程序中调用类初始化程序,可以在纯 Objective-C 中重现此行为。

@implementation NSTimer (Foo)

- (instancetype)initWithTarget:(id)t {
    return [NSTimer timerWithTimeInterval:1 target:t selector:@selector(description) userInfo:nil repeats:NO];
}

@end

编译器抱怨没有调用指定的初始化程序,这似乎与解决问题非常相关,但没有解释递归调用。

warning: convenience initializer missing a 'self' call to another initializer
于 2016-12-24T23:04:09.700 回答
0

@matt 的答案有效。

是的,我在我的应用程序中也看到了无限递归——CFReleases。Swift 的书很清楚,便利构造器必须调用指定构造器。不过,它并没有说明惩罚是什么。无限递归虽然令人惊讶,但似乎是合理的。

但是,查看这两个声明,您可以通过在 Xcode 中通过选项单击或命令单击这些方法之一来查看它们:

init(timeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void)

convenience init(fire date: Date, interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void)

我认为那里有问题。@matt 建议的函数,带有附加的 ("fire") 参数,它为我解决了这个问题,被标记为便利。我使用的功能,没有标记为便利,我认为(作为 Swift 新手)因此被指定。但它少了一个参数。嗯?

我想我应该提交一个错误,说明Apple 以某种方式在错误的功能上获得了便利关键字。这可能是因为 Swift 真的没有头文件,对吗?所以我想知道当我们选择单击 Foundation 函数时会看到什么。可能他们的工作流程中有一个步骤容易受到人为错误的影响?

于 2016-12-25T07:46:17.840 回答