将结构属性转换为字典的更有效方法

yer*_*rpy 1 reflection dictionary swift

我一直在寻找从结构创建字典的最有效方法。经过细致的研究,我发现了一种通过初始化所有属性来转换它的简便方法:

func toDictionary() -> [String : AnyObject] {
    let dictionary: [String: AnyObject] = ["firstProperty" : sth, "secondProperty" : "sth2"]
    return dictionary
}
Run Code Online (Sandbox Code Playgroud)

但是,等等。我是否必须在结构中每次都初始化它?他们可能有很多财产。.这让我思考。如果我可以通过类的循环获得属性怎么办。是的,可以使用Mirror反射。经过一段时间的尝试,我明白了—而不是上面的功能,我编写了实现一个功能的协议。

protocol Mirrorable {
    func toDictionary() -> [String : AnyObject]
}
Run Code Online (Sandbox Code Playgroud)

然后在我的结构中,我可以简单地使用:

extension MyStructure/MyClass : Mirrorable {

    func toDictionary() -> [String : AnyObject] {
        let reflection = Mirror(reflecting: self).children
        var dictionary = [String : AnyObject]()
        for (label, value) in reflection {
            dictionary[label!] = value as AnyObject
        }
        return dictionary
    }
}
Run Code Online (Sandbox Code Playgroud)

好奇心并没有让我停止思考。哪种方法更有效?

hen*_*que 5

TLDR

直接转换速度更快,但是由于大多数情况下您不必将1.000.000+结构转换为字典,因此我将使用协议扩展。


长答案

我创建了一个Mac cmd line应用程序来测试时间,并且正如预期的那样,直接转换更快。这很明显,因为编译器可以优化它,并且代码本身很简单。使用反射时,您会创建大量额外的结构和变量,还增加了一些开销。

尽管直接转换更快,但我认为在大多数实际情况下,使用协议扩展方法会很好,因为它很容易在您的代码库中采用。您必须记住的一件事是,使用反射的代码未考虑嵌套对象,这会使该方法变得更慢,并且如果您使用递归将产生更大的开销。

以下是3次执行的结果,这些执行将1.000.000结构编码为字典:

构建设置

  • Apple LLVM 8.1-代码生成

    • 优化级别:-Ofast
  • Swift编译器-代码生成:

    • 优化级别:-O-整个模块的优化

使用S1(直接转换)执行1000000次的时间:0.569061994552612

使用S2(结构扩展+协议)执行1000000次的时间:7.68360501527786

使用S3(协议扩展)执行1000000次的时间:7.71803396940231

使用S1(直接转换)执行1000000次的时间:0.500779032707214

使用S2(结构扩展+协议)执行1000000次的时间:7.58478999137878

使用S3(协议扩展)执行1000000次的时间:7.73368299007416

使用S1(直接转换)执行1000000次的时间:0.492152035236359

使用S2(结构扩展+协议)执行1000000次的时间:7.81585901975632

使用S3(协议扩展)执行1000000次的时间:7.41855001449585

构建设置

  • Apple LLVM 8.1-代码生成

    • 优化级别:-Ofast
  • Swift编译器-代码生成:

    • 优化级别:-Onone

使用S1(直接转换)执行1000000次的时间:2.92627400159836

使用S2(结构扩展+协议)执行1000000次的时间:9.25952398777008

使用S3(协议扩展)执行1000000次的时间:9.19355899095535

使用S1(直接转换)执行1000000次的时间:2.9830749630928

使用S2(结构扩展+协议)执行1000000次的时间:9.06750500202179

使用S3(协议扩展)执行1000000次的时间:8.77240401506424

使用S1(直接转换)执行1000000次的时间:2.81389397382736

使用S2(结构扩展+协议)执行1000000次的时间:8.84287703037262

使用S3(协议扩展)执行1000000次的时间:9.08754301071167

构建设置

  • Apple LLVM 8.1-代码生成

    • 优化级别:-O0
  • Swift编译器-代码生成:

    • 优化级别:-O-整个模块的优化

使用S1(直接转换)执行1000000次的时间:0.533200979232788

使用S2(结构扩展+协议)执行1000000次的时间:8.15365797281265

使用S3(协议扩展)执行1000000次的时间:7.80043601989746

使用S1(直接转换)执行1000000次的时间:0.509769976139069

使用S2(结构扩展+协议)执行1000000次的时间:7.76911997795105

使用S3(协议扩展)执行1000000次的时间:8.00845402479172

使用S1(直接转换)执行1000000次的时间:0.532546997070312

使用S2(结构扩展+协议)执行1000000次的时间:7.99552202224731

使用S3(协议扩展)执行1000000次的时间:7.86273497343063

构建设置

  • Apple LLVM 8.1-代码生成

    • 优化级别:-O0
  • Swift编译器-代码生成:

    • 优化级别:-Onone

使用S1(直接转换)执行1000000次的时间:2.90398299694061

使用S2(结构扩展+协议)执行1000000次的时间:9.62662398815155

使用S3(协议扩展)执行1000000次的时间:9.55038601160049

使用S1(直接转换)执行1000000次的时间:2.98312002420425

使用S2(结构扩展+协议)执行1000000次的时间:9.62088203430176

使用S3(协议扩展)执行1000000次的时间:8.82720899581909

使用S1(直接转换)执行1000000次的时间:2.77569997310638

使用S2(结构扩展+协议)执行1000000次的时间:8.83749902248383

使用S3(协议扩展)执行1000000次的时间:8.76373296976089

码:

import Foundation

// Direct conversion to dictionary
struct S1 {
    var a: Int
    var b: Int
    var c: Int
    func toDictionary() -> [String : AnyObject] {
        let dictionary: [String: AnyObject] = [
            "a":a as AnyObject,
            "b":b as AnyObject,
            "c":c as AnyObject
        ]
        return dictionary
    }
}

// Conversion using struct extension + protocol
protocol Mirrorable1 {
    func toDictionary() -> [String : AnyObject]
}

struct S2 {
    var a: Int
    var b: Int
    var c: Int
}

extension S2: Mirrorable1 {
    func toDictionary() -> [String : AnyObject] {
        let reflection = Mirror(reflecting: self).children
        var dictionary = [String : AnyObject]()
        for (label, value) in reflection {
            dictionary[label!] = value as AnyObject
        }
        return dictionary
    }
}

// Conversion using protocol extension
protocol Mirrorable2 {
    func toDictionary() -> [String : AnyObject]
}

extension Mirrorable2 {
    func toDictionary() -> [String : AnyObject] {
        let reflection = Mirror(reflecting: self).children
        var dictionary = [String : AnyObject]()
        for (label, value) in reflection {
            dictionary[label!] = value as AnyObject
        }
        return dictionary
    }
}

struct S3: Mirrorable2 {
    var a: Int
    var b: Int
    var c: Int
}

let listOfExecutions = [1_000_000, 1_000_000, 1_000_000]
for executions in listOfExecutions {
    // S1
    var start = CFAbsoluteTimeGetCurrent()
    for i in 0..<executions {
        S1(a: 0, b: 1, c: 2).toDictionary()
    }
    print("Time for \(executions) executions with S1(direct conversion):")
    print(CFAbsoluteTimeGetCurrent() - start)

    // S2
    start = CFAbsoluteTimeGetCurrent()
    for i in 0..<executions {
        S2(a: 0, b: 1, c: 2).toDictionary()
    }
    print("Time for \(executions) executions with S2(struct extension + protocol):")
    print(CFAbsoluteTimeGetCurrent() - start)

    // S3
    start = CFAbsoluteTimeGetCurrent()
    for i in 0..<executions {
        S3(a: 0, b: 1, c: 2).toDictionary()
    }
    print("Time for \(executions) executions with S3(protocol extension):")
    print(CFAbsoluteTimeGetCurrent() - start)
}
Run Code Online (Sandbox Code Playgroud)