Xcode 9.3 (Swift 4.1)中,Codable没有初始化程序。

37

Xcode 9.3(使用Swift 4.1)更新后,发现以下问题:

  1. 创建一个空项目,在其中添加一个新的.swift文件并创建两个新类:

    Created to Codable classes

class CodableOne: Codable {

    let some: String

}

class CodableTwo: Codable {

    var some: String

}

构建成功

  • CodableOne添加一个新的常量,类型为CodableTwo

    向CodableOne添加新常量

    class CodableOne: Codable {
    
        let some: String
        let another: CodableTwo
    
    }
    
    class CodableTwo: Codable {
    
        var some: String
    
    }
    

    构建成功

  • 现在将类CodableTwo移动到另一个文件中(例如ViewController.swift)

    CodableTwo moved to another file

    构建失败

  • 现在出现了一个错误,这个错误不会消失。 Codable 类不应该需要初始化程序(如前面的步骤所示)。

    有没有什么想法,可能导致问题的出现以及如何解决问题,将不胜感激!


    P.S. 在Xcode 9.2中不存在此问题。清理项目/构建路径、重新安装Xcode 9.3都无法解决。


    9
    不错的发现 - 已提交错误报告:https://bugs.swift.org/browse/SR-7315 - Hamish
    2
    @Hamish - 这对我来说似乎过早了。打开“整个模块”编译。 - Rob
    2
    @Rob 对我来说没有任何区别(编辑:哦,看起来这取决于“编译源”中文件的顺序)。虽然无论如何都不应该有任何区别——编译器在整个模块编译下不应该给出不同的行为(它的目的是允许更激进的优化)。 - Hamish
    1
    我和@Hamish一样,看起来像是编译器的错误。感谢您打开它。 - Rob Napier
    1
    整个模块的编译对我来说没有任何影响。由于9.3.Great的原因,我现在无法工作。 - nickdnk
    显示剩余11条评论
    4个回答

    24

    正如评论中所提到的,我需要做两件事:

    1. 项目设置/构建设置中将编译模式更改为整个模块(Whole Module)

      Compilation Mode set to Whole Module

    2. 重新排序项目设置/构建阶段/编译源文件(Compile Sources)下面的文件。具体来说,我将出现错误的文件移到了列表前面

      提示:如果您搜索文件名并且有多个结果,则在较小的列表中将文件拖到顶部仍会将其移到前面。


    1
    那篇文章中的步骤已经过时了,因为 Xcode 9.3 中该选项已被移动并更名。现在它在“编译模式”下命名为“整个模块”。另外,我不会给你赏金,因为你的答案就是我在赏金标题中说过不起作用的确切步骤 :P - Oscar Apeland
    1
    @OscarApeland 谢谢您提供的信息,我已经进行了编辑。在回答时我并不知道您的悬赏,我只是试图整理我发现可行的解决方案。此外,在评论中没有提到将带有错误的文件放在前面。我完全反对剽窃和从评论中复制粘贴答案,我总是添加一些额外的信息(如果没有,我会将我的答案标记为社区 wiki)。 - Tamás Sengel
    1
    没问题,非常好的答案,显然对一些人有效 :) JIRA bug 刚刚被分配给了苹果公司的某个家伙,希望我们很快能得到一些真正的答案。 - Oscar Apeland
    不幸的是,当我想在Objective C中创建Swift对象时,这并不起作用。我尝试将Swift文件重新排序到顶部,然后将ObjC类移到顶部,但都没有起作用。 - Josip B.

    21

    这是 Swift 4.1 编译器中的一个漏洞。要解决此问题,请执行 the4kman 回答中概述的步骤或仅在声明中将 let 更改为 var,如下所示:

    class C1 : Decodable { 
      let str: String 
      // error: Class 'C1' has no initializers - if class C's `c1` is a let constant. 
    }
    
    class C : Decodable {
      var c1: C1 // << Change to `var`, compilation succeeds.
    }
    

    由于苹果的 Swift 工程师提供的方法,可以解决此问题。 点击此处 查看详情。

    如果上述方法和 the4kmans 的回答都没有帮助到你,你可以为那些无法编译的模型添加另一个 init。如果你的类有很多变量,只需使 init 崩溃以满足编译器。Codable 初始化程序仍将被合成。

    class C1: Decodable {
        let str: String
    
        @available(*, deprecated, message: "Do not use.")
        private init() {
            fatalError("Swift 4.1") 
        }
    }
    

    谢谢,这个方法有效。有趣的是,我的一些Codable类通过上面建议的项目设置更改得到了修复,而其他一些则需要这个修复方法。 - Halyna Rubashko
    是的,看起来需要一些综合考虑。希望他们能尽快解决这个问题。 - Oscar Apeland
    它可以工作。如果您将init标记为“private”,则无需担心可用性。 - manueGE
    @manueGE 这样做更聪明。我忘记了初始化器有访问控制。已更新答案。 - Oscar Apeland

    0

    我曾经遇到过这个问题,即使我的所有类都在同一个文件中,但对于更深层次的结构体使用似乎可以解决问题。


    这是因为结构体会自动合成另一个初始化方法。 - Oscar Apeland

    -3
    尝试给你的变量赋一个初始值,像这样(将你的代码改成这样)
    class CodableOne: Codable{
    
        var some = ""
    
    }
    
    class CodableTwo: Codable{
    
        var some = ""
    
    }
    

    2
    不行,你不能这样做。这会覆盖解码后的值。 - Oscar Apeland

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接