在尝试使用基本JSONEncoder序列化float3对象数组时,发现float3不符合Codable协议,因此无法完成。
我尝试按照Encoding and Decoding Custom Types中的建议编写基本扩展,如下所示,但是为init中的每个赋值行呈现错误'self' used before all stored properties are initialized
。我假设这是因为编译器不确定在初始化float3之前定义了Float.self
,但我不知道如何解决这个问题。
此外,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)
}
答案 0 :(得分:9)
您可以解决编译器错误,而不是尝试直接将解码值分配给您的类型字段,将解码后的值存储在局部变量中,然后调用float3
的指定初始值设定项。
正如Rob在his answer中提到的那样,问题的原因与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))
}
答案 1 :(得分:4)
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]
}
}
如果你针对它编写扩展名,你会得到同样的错误。
答案 2 :(得分:3)
我认为值得一提的是,你可以简单地使用一个无键的容器来编码/解码你的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])
}
}