在尝试使用基本 JSONEncoder 序列化 float3 对象数组时,发现 float3 不符合 Codable 协议,因此无法完成此操作。
我尝试按照编码和解码自定义类型中的建议编写基本扩展,如下所示,但是为 init 中的每个赋值行呈现错误
'self' used before all stored properties are initialized
。我认为这是因为编译器不确定 Float.self
是在 float3 初始化之前定义的,但我不确定如何解决这个问题。
此外,init 的末尾显示
Return from initializer without initializing all stored properties
,我认为这意味着除了 x、y 和 z 之外还有 float3 属性,但我想知道是否有办法默认/忽略这些属性,和/或如何除了深入研究 simd 中的所有 float3 扩展之外,还可以找到完整的属性列表。
如果您对如何执行此操作有任何想法,我们将不胜感激。谢谢!
import SceneKit
extension float3: Codable {
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
x = try values.decode(Float.self, forKey: .x)
y = try values.decode(Float.self, forKey: .y)
z = try values.decode(Float.self, forKey: .z)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(x, forKey: .x)
try container.encode(y, forKey: .y)
try container.encode(z, forKey: .z)
}
enum CodingKeys: String, CodingKey {
case x
case y
case z
}
}
这是 simd 文件中的(我认为完整的)float3 定义:
/// A vector of three `Float`. This corresponds to the C and
/// Obj-C type `vector_float3` and the C++ type `simd::float3`.
public struct float3 {
public var x: Float
public var y: Float
public var z: Float
/// Initialize to the zero vector.
public init()
/// Initialize a vector with the specified elements.
public init(_ x: Float, _ y: Float, _ z: Float)
/// Initialize a vector with the specified elements.
public init(x: Float, y: Float, z: Float)
/// Initialize to a vector with all elements equal to `scalar`.
public init(_ scalar: Float)
/// Initialize to a vector with elements taken from `array`.
///
/// - Precondition: `array` must have exactly three elements.
public init(_ array: [Float])
/// Access individual elements of the vector via subscript.
public subscript(index: Int) -> Float
}
extension float3 : Equatable {
/// True iff every element of lhs is equal to the corresponding element of
/// rhs.
public static func ==(lhs: float3, rhs: float3) -> Bool
}
extension float3 : CustomDebugStringConvertible {
/// Debug string representation
public var debugDescription: String { get }
}
extension float3 : ExpressibleByArrayLiteral {
/// Initialize using `arrayLiteral`.
///
/// - Precondition: the array literal must exactly three
/// elements.
public init(arrayLiteral elements: Float...)
}
extension float3 : Collection {
/// The position of the first element in a nonempty collection.
///
/// If the collection is empty, `startIndex` is equal to `endIndex`.
public var startIndex: Int { get }
/// The collection's "past the end" position---that is, the position one
/// greater than the last valid subscript argument.
///
/// When you need a range that includes the last element of a collection, use
/// the half-open range operator (`..<`) with `endIndex`. The `..<` operator
/// creates a range that doesn't include the upper bound, so it's always
/// safe to use with `endIndex`. For example:
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let index = numbers.index(of: 30) {
/// print(numbers[index ..< numbers.endIndex])
/// }
/// // Prints "[30, 40, 50]"
///
/// If the collection is empty, `endIndex` is equal to `startIndex`.
public var endIndex: Int { get }
/// Returns the position immediately after the given index.
///
/// The successor of an index must be well defined. For an index `i` into a
/// collection `c`, calling `c.index(after: i)` returns the same index every
/// time.
///
/// - Parameter i: A valid index of the collection. `i` must be less than
/// `endIndex`.
/// - Returns: The index value immediately after `i`.
public func index(after i: Int) -> Int
}
extension float3 {
/// Vector (elementwise) sum of `lhs` and `rhs`.
public static func +(lhs: float3, rhs: float3) -> float3
/// Vector (elementwise) difference of `lhs` and `rhs`.
public static func -(lhs: float3, rhs: float3) -> float3
/// Negation of `rhs`.
prefix public static func -(rhs: float3) -> float3
/// Elementwise product of `lhs` and `rhs` (A.k.a. the Hadamard or Schur
/// vector product).
public static func *(lhs: float3, rhs: float3) -> float3
/// Scalar-Vector product.
public static func *(lhs: Float, rhs: float3) -> float3
/// Scalar-Vector product.
public static func *(lhs: float3, rhs: Float) -> float3
/// Elementwise quotient of `lhs` and `rhs`.
public static func /(lhs: float3, rhs: float3) -> float3
/// Divide vector by scalar.
public static func /(lhs: float3, rhs: Float) -> float3
/// Add `rhs` to `lhs`.
public static func +=(lhs: inout float3, rhs: float3)
/// Subtract `rhs` from `lhs`.
public static func -=(lhs: inout float3, rhs: float3)
/// Multiply `lhs` by `rhs` (elementwise).
public static func *=(lhs: inout float3, rhs: float3)
/// Divide `lhs` by `rhs` (elementwise).
public static func /=(lhs: inout float3, rhs: float3)
/// Scales `lhs` by `rhs`.
public static func *=(lhs: inout float3, rhs: Float)
/// Scales `lhs` by `1/rhs`.
public static func /=(lhs: inout float3, rhs: Float)
}
您可以通过以下方式解决编译器错误:直接将解码值分配给类型的字段,将解码值存储在局部变量中,然后调用指定的初始化器
float3
。
正如 Rob 在他的回答中提到的,问题的原因与
x
、y
和 z
是计算属性而不是存储属性有关,因此它们不能在初始化期间直接写入。
extension float3: Codable {
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let x = try values.decode(Float.self, forKey: .x)
let y = try values.decode(Float.self, forKey: .y)
let z = try values.decode(Float.self, forKey: .z)
self.init(x, y, z)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(x, forKey: .x)
try container.encode(y, forKey: .y)
try container.encode(z, forKey: .z)
}
private enum CodingKeys: String, CodingKey {
case x,y,z
}
}
您可以使用以下代码在 Playground 中测试编码和解码:
let vector = float3(3, 2.4, 1)
do {
let encodedVector = try JSONEncoder().encode(vector)
let jsonVector = String(data: encodedVector, encoding: .utf8) //"{"x":3,"y":2.4000000953674316,"z":1}"
let decodedVector = try JSONDecoder().decode(float3.self, from: encodedVector) //float3(3.0, 2.4, 1.0)
} catch {
print(error)
}
如果您喜欢更简洁的代码,
init(from decoder:)
方法可以缩短为:
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
try self.init(values.decode(Float.self, forKey: .x), values.decode(Float.self, forKey: .y), values.decode(Float.self, forKey: .z))
}
Dávid 对于如何解决问题是正确的,但值得理解为什么这是一个问题(它实际上不是指定的与方便的初始化器;这只适用于类)。
如果我们创建了自己的
float3
版本,您的代码可以与扩展一起正常工作:
struct float3 {
var x: Float
var y: Float
var z: Float
}
extension float3: Codable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
x = try values.decode(Float.self, forKey: .x)
y = try values.decode(Float.self, forKey: .y)
z = try values.decode(Float.self, forKey: .z)
}
// ...
}
所以这看起来很奇怪。为什么我们的
simd
类型的实现与 simd
类型的工作方式不同?因为 simd
没有 x
存储属性(或 y
或 z
)。这些都是计算属性。
simd.swift.gyb
中定义。如果您研究该代码,您将看到所有 SIMD 向量类型都使用通用 _vector
存储:
public var _vector: Builtin.${llvm_vectype}
然后为每个字母定义计算属性(
component
是 ['x','y','z','w']
):
% for i in xrange(count):
public var ${component[i]} : ${scalar} {
@_transparent
get {
let elt = Builtin.${extractelement}(_vector,
(${i} as Int32)._value)
return ${scalar}(_bits: elt)
}
@_transparent
set {
_vector = Builtin.${insertelement}(_vector,
newValue._value,
(${i} as Int32)._value)
}
}
% end
因此,如果我们构建自己的
float3
(没有花哨的内置函数),它会是这样的:
struct float3 {
private var vector: [Float]
var x: Float { get { return vector[0] } set { vector[0] = newValue } }
var y: Float { get { return vector[1] } set { vector[1] = newValue } }
var z: Float { get { return vector[2] } set { vector[2] = newValue } }
init(x: Float, y: Float, z: Float) {
vector = [x, y, z]
}
}
如果你编写扩展程序,你会得到同样的错误。
我认为值得一提的是,您可以简单地使用未加密的容器将 float3 属性编码/解码为数组:
extension float3: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
try self.init(container.decode([Float].self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode([x,y,z])
}
}