Xcode 警告:不可变属性不会被解码,因为它是用无法覆盖的初始值声明的

问题描述 投票:0回答:5

运行 Xcode 12,我的 Swift 5 Xcode 项目现在只要

Decodable
Codable
类型声明具有初始值的
let
常量就会发出警告。

struct ExampleItem: Decodable {
    let number: Int = 42 // warning
}

不可变属性不会被解码,因为它是用不能被覆盖的初始值声明的

Xcode 建议将

let
更改为
var
:

修复:改为使属性可变

    var number: Int = 42

它还建议修复:

修复:通过初始化程序设置初始值或显式定义一个包含“标题”大小写的 CodingKeys 枚举来消除此警告

这个新警告的目的是什么?是应该重视,还是忽视?这种类型的警告可以被静音吗

Xcode 的修复是否应该实施?或者有更好的解决办法吗?

swift xcode warnings xcode12
5个回答
54
投票

诺亚的解释是正确的。这是错误的常见来源,并且由于可编码合成的“神奇”行为,发生的情况并不是很明显,这就是为什么我向编译器添加了此警告,因为它让您注意到该属性不会不会被解码并让您明确地调用它(如果这是预期的行为)。 正如修复程序所解释的那样,如果您想消除此警告,您有多种选择 - 您选择哪一个取决于您想要的确切行为:


通过
    init
  1. 传递初始值:
    
    
  2. struct ExampleItem: Decodable { let number: Int init(number: Int = 42) { self.number = number } }
这将允许解码 
number

,但您也可以传递使用默认值的

ExampleItem
实例。
您也可以直接在

init

内部使用它,在解码过程中:

struct ExampleItem: Decodable {
  let number: Int
    
  private enum CodingKeys: String, CodingKey {
    case number
  }
    
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    number = try container.decodeIfPresent(Int.self, forKey: .number) ?? 42
  }
}

这将允许解码 
number

,但如果解码失败,则使用

42
作为默认值。


将属性设为
    var
  1. ,尽管您也可以将其设为
    private(set) var
    :
    
    
  2. struct ExampleItem: Decodable { var number: Int = 42 }
将其设为 
var

将允许

number
被解码,但也允许调用者对其进行修改。通过将其标记为
private(set) var
,您可以根据需要禁止此操作。


定义显式
    CodingKeys
  1. 枚举:
    
    
  2. struct ExampleItem: Decodable { let number: Int = 42 private enum CodingKeys: CodingKey {} }
这将阻止 
number

被解码。由于枚举没有大小写,这使编译器清楚地知道没有要解码的属性。

    


7
投票

例如,考虑以下代码:

struct Model: Decodable { let value: String = "1" } let json = """ {"value": "2"} """ let decoder = JSONDecoder() let model = try! decoder.decode(Model.self, from: json.data(using: .utf8)!) print(model)

这实际上会打印 
Model(value: "1")

,即使我们给它的 json 的

value
"2"
事实上,您甚至不需要提供正在解码的数据中的值,因为它无论如何都有一个初始值!

let json = """ {} """ let decoder = JSONDecoder() let model = try! decoder.decode(Model.self, from: json.data(using: .utf8)!) print(model) // prints "Model(value: "1")"

将值更改为 var 意味着它将正确解码:

struct VarModel: Decodable { var value: String = "1" } let json = """ {"value": "2"} """ let varModel = try! decoder.decode(VarModel.self, from: json.data(using: .utf8)!) print(varModel) // "VarModel(value: "2")"

如果您看到此错误,则意味着您的代码在解码时从未正确解析有问题的属性。如果将其更改为 var,该属性将被正确解析,这可能就是您想要的 - 但是,您应该确保您正在解码的数据始终具有该键集。例如,这将引发异常(并且由于我们使用的是 
try!

而崩溃):

let json = """
{}
"""
let decoder = JSONDecoder()

struct VarModel: Decodable {
    var value: String = "1"
}

let varModel = try! decoder.decode(VarModel.self, from: json.data(using: .utf8)!)

总之,Xcode 的建议在许多情况下可能是可行的,但您应该根据具体情况进行评估,将属性更改为 
var

是否会破坏应用程序的功能。

如果您希望属性始终返回硬编码的初始值(这就是现在正在发生的情况),请考虑将其设为计算属性或惰性变量。


4
投票
解决方案:

定义一个显式的CodingKeys枚举以防止

id
被解码。 例如,
struct Course: Identifiable, Decodable {
  let id = UUID()
  let name: String

  private enum CodingKeys: String, CodingKey {
    case name
  }
  
  init(name: String) { self.name = name }
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let name = try container.decodeIfPresent(String.self, forKey: .name)
    self.name = name ?? "default-name"
  }
}



2
投票
here

public struct IdentifierWrapper<T>: Identifiable { public let id = UUID() public let value: T }

用途:

struct Model: Codable, Identifiable { public let name: String } let wrapper = IdentifierWrapper(value: Model(name: "ptrkstr"))



0
投票

struct ExampleItem: Decodable { var number: Int { 42 } // no warning anymore }

行为保持不变 - 解码器不会干扰 
number

字段。而且您不需要实现自定义

CodingKeys
    

© www.soinside.com 2019 - 2024. All rights reserved.