子类中的委托方法有时不使用 Swift 5 编译器调用

Posted

技术标签:

【中文标题】子类中的委托方法有时不使用 Swift 5 编译器调用【英文标题】:Delegate methods in child class sometimes not called with Swift 5 compiler 【发布时间】:2019-08-18 22:54:06 【问题描述】:

编辑:正如sunshinejr 指出的here,此问题已得到修复,并将与下一个 Xcode/Swift 版本一起发布。


在使用 Swift 4 和 Swift 5 代码库将 Xcode 10.1 更新到 Xcode 10.2 后,我看到了很多奇怪的行为。

其中一个问题是在一个 ViewController 上不再调用 ScrollView 委托方法。简化的视图层次结构如下:

| ScrollView (ParentScrollView)
| -- Stack View
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)

它充当具有多个页面的视图:ParentScrollView 可以水平滚动,ChildScrollViews 可以垂直滚动。

ViewController 是所有 Scrollview 的委托(在 Storyboard 中设置),但在滚动任何视图(ParentScrollView 或 ChildScrollView)时不会调用委托方法(如 scrollViewDidEndDecelerating)。 ViewController基类符合UIScrollViewDelegate

我尝试在代码中设置代表,除此之外我不知道我可能做错了什么。转换没有更改类中的任何代码,但在更新之前一切正常。我在Swift 5 Release Notes 中也找不到对手势、委托或滚动视图的一般更改。

这似乎是 Swift 5 编译器的一个错误。此外,有时它确实有效,有时则无效——所有这些都无需更改任何代码或项目设置。

为什么这不再有效?有没有其他人经历过类似的行为?

【问题讨论】:

无关:应该有比嵌套滚动视图更好的方法来做你想做的事情。 @RakeshaShastri 嗯,也许作为子视图控制器? 也许吧。我不知道,因为我不知道它们是干什么用的。否决票可能是因为您没有表明问题太模糊。 P.S - 我没有投反对票。 这根本不应该被否决!这是最新 Xcode / Swift 编译器的重大变化 @JanSchlorf 您能否更新您的编辑,使用 Xcode 和 Swift 的确切版本修复问题,而不仅仅是说“下一个 Xcode/Swift 版本”? 【参考方案1】:

升级到 Xcode 10.2 后,我仅在发布方案中遇到了同样的问题。我还测试了 Xcode 10.3,它的行为完全相同。

对于那些不想在委托实现中到处添加@objc 的人。

快速解决方案是在构建设置中禁用 Swift 5 编译器优化:

对于已经升级到 Xcode 10.3 的用户,这个构建设置选项似乎不再可见,但您仍然可以通过项目的 pbxproj 文件直接更改它,之后它应该会出现在 xcode UI 中。 SWIFT_COMPILATION_MODE = singlefile;

【讨论】:

【参考方案2】:

正如 Jan 指出的,这是 Swift 5 的回归。这在 Swift 的JIRA 以及雷达 (rdar://problem/49482328) 上进行了跟踪。这也已经修复 (PR here) 但我们需要等待下一个 Xcode/Swift 版本。

编辑:从 Xcode 10.3 开始,我们观察到该错误已修复,但我们仍在监控它是否已永久修复。

【讨论】:

提到的 PR 说它将发布 Swift 5.1,它应该随 Xcode 11(Beta)而不是 10.3 一起发布。【参考方案3】:

编辑:正如sunshinejr 指出的here,此问题已得到修复,并将与下一个 Xcode/Swift 版本一起发布。


我找到了问题,这里是重现它的方法。

class A: UIViewController, UIScrollViewDelegate 
    // ...does not implement 'scrollViewDidEndDecelerating'


class B: A 
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
        // Will not be called!
    

有什么作用:

class A: UIViewController, UIScrollViewDelegate 
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
        // Probably empty
    


class B: A 
    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
        // Will be called!
    

如果基类没有实现委托方法,编译器似乎认为它没有实现。如果只有子类实现的话,是找不到的。

我仍然无法解释为什么 Swift 5 改变了这种行为,但至少我找到了解决方案。也许有人可以提供进一步的见解?

【讨论】:

我可以确认这是问题所在。它在我们的应用程序以及我们昨天升级的过程中造成了重大错误。 这个 Swift 5 编译器错误可能适用于所有未在关联基类中实现的委托方法。更糟糕的是,这个错误有时只在发布版本中表现出来。 @ClausJørgensen 您现在找到解决方案了吗?我现在正在使用以前版本的 Xcode 进行构建,因为试图在我们的项目中找到所有可能的错误情况实在是太冒险了。 @JanSchlorf 您的解决方案工作得很好。如果您考虑一下 Objective-C 选择器的工作原理,就会发现这个 bug 可能会发生。 有人请提交雷达 (developer.apple.com/bug-reporting) 并在此处链接!【参考方案4】:

看起来这个问题早在 2016 年就已经存在,并在某一时刻得到了修复:https://bugs.swift.org/browse/SR-2919

【讨论】:

我花时间去那里,创建一个帐户并重新打开它。给它一些赞成票,让它受到关注。在生产就绪的编译器中,这是一个非常严重的问题。【参考方案5】:

我们使用 UITextViewDelegate 遇到了这个问题

另一种解决方法是将@objc标签添加到超类中的方法

【讨论】:

这对我有用,即使我所有的相关代码都在 swift 中。我认为它之所以有效,是因为它引入了动态查找/动态调度。【参考方案6】:

由于所有 UIScrollViewDelegate 方法都是可选的,如果编译器认为你没有实现它们,你将永远不会看到来自编译器的错误,最有可能发生的事情是 Apple 更改了 Swift 5 中的方法签名(再次)和迁移工具不起作用的某种原因。 检查方法名称以及更新到 USwift 5 文档的 UIScrollViewDelegate,您会发现您的方法名称可能不同,只需更正它们,一切都会再次正常运行。

【讨论】:

感谢您的回答。命名是正确的,但我赞成它,因为它可能对未来的读者有用。 这个答案可能有用,但正确答案是添加@objc。方法签名是正确的。

以上是关于子类中的委托方法有时不使用 Swift 5 编译器调用的主要内容,如果未能解决你的问题,请参考以下文章

需要在 Swift 中的 UITextField 委托方法中引用自定义 UITableViewCell 而不使用 tag 属性

对象在 Swift 中不符合 kvc 时的对象通信

通过 Swift 中的不同类调用委托方法

Swift UI 委托方法不起作用

《你不知道的JS(中卷)》行为委托

UIView 类中的 Swift 集合视图委托