Swift 3:便利初始化程序扩展基础 'Timer' 挂起
Swift 3: Convenience Initializer Extending Foundation's 'Timer' Hangs
我正在尝试在 Swift 3 中扩展 Foundation 的 Timer
class,添加一个方便的初始值设定项。但它对基金会提供的初始化程序的调用从不 returns.
问题在下面的简单演示中得到了说明,可以运行作为游乐场。
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)
控制台输出:
Next statement never returns
进一步研究,
• 我编写了扩展 URLProtocol
的类似代码(仅有的其他具有实例初始化器的 classes 之一)。结果:没问题。
• 为了消除 Objective-C 的可能原因,我将包装的初始值设定项更改为 init(timeInterval:repeats:block:)
方法并提供了一个 Swift 闭包。结果:同样的问题。
我实际上并不知道答案,但我从 运行 中看到,在带有调试器的实际应用程序中存在无限递归(因此挂起)。我 怀疑 这是因为您实际上并没有调用 Timer 的指定初始化程序。这个事实并不明显,但是如果您尝试子类化 Timer 并调用 super.init(timeInterval...)
,编译器会报错,而且 header 中的 super.init(timeInterval...)
上还有一个奇怪的 "not inherited" 标记。
我可以通过调用 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
}
}
随心所欲...
我看到了与 matt 描述的无限递归相同的问题。 -[NSCFTimer release]
在同一个对象上被一遍又一遍地调用。通过从实例初始化程序中调用 class 初始化程序,可以在纯 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
@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") 参数,为我解决了这个问题,标记为 convenience。我使用的函数 not 标记为 convenience,因此我认为(作为 Swift 新手)是 指定。但是它少了一个参数。嗯?
我想我应该提交一个错误,说明 Apple 以某种方式在错误的函数 上获得了 convenience 关键字。这可能是因为 Swift 真的没有头文件,对吗?所以我想知道当我们选择单击 Foundation 函数时我们会看到什么。可能他们的工作流程中有一个容易出现人为错误的步骤?
我正在尝试在 Swift 3 中扩展 Foundation 的 Timer
class,添加一个方便的初始值设定项。但它对基金会提供的初始化程序的调用从不 returns.
问题在下面的简单演示中得到了说明,可以运行作为游乐场。
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)
控制台输出:
Next statement never returns
进一步研究,
• 我编写了扩展 URLProtocol
的类似代码(仅有的其他具有实例初始化器的 classes 之一)。结果:没问题。
• 为了消除 Objective-C 的可能原因,我将包装的初始值设定项更改为 init(timeInterval:repeats:block:)
方法并提供了一个 Swift 闭包。结果:同样的问题。
我实际上并不知道答案,但我从 运行 中看到,在带有调试器的实际应用程序中存在无限递归(因此挂起)。我 怀疑 这是因为您实际上并没有调用 Timer 的指定初始化程序。这个事实并不明显,但是如果您尝试子类化 Timer 并调用 super.init(timeInterval...)
,编译器会报错,而且 header 中的 super.init(timeInterval...)
上还有一个奇怪的 "not inherited" 标记。
我可以通过调用 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
}
}
随心所欲...
我看到了与 matt 描述的无限递归相同的问题。 -[NSCFTimer release]
在同一个对象上被一遍又一遍地调用。通过从实例初始化程序中调用 class 初始化程序,可以在纯 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
@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") 参数,为我解决了这个问题,标记为 convenience。我使用的函数 not 标记为 convenience,因此我认为(作为 Swift 新手)是 指定。但是它少了一个参数。嗯?
我想我应该提交一个错误,说明 Apple 以某种方式在错误的函数 上获得了 convenience 关键字。这可能是因为 Swift 真的没有头文件,对吗?所以我想知道当我们选择单击 Foundation 函数时我们会看到什么。可能他们的工作流程中有一个容易出现人为错误的步骤?