I was looking for most efficient way for creating dictionary from structures. After small research I found easy way for converting it via initialising all properties like that :
func toDictionary() -> [String : AnyObject] {
let dictionary: [String: AnyObject] = ["firstProperty" : sth, "secondProperty" : "sth2"]
return dictionary
}
But wait.. Do I have to initialise it every time in my structures ? They might have a loot of properties.. It gave me to think. What if I could get properties though the looping of the class somehow. Yeah, it is possible using Mirror
reflection. After a while of trying I've got it - instead of function above I've written protocol which implements one function.
protocol Mirrorable {
func toDictionary() -> [String : AnyObject]
}
Then in my structure I can simply use :
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
}
}
Curiosity does not let me to stop thinking about it. Which way would be more efficient ?
TLDR
Direct conversion is faster but since most cases you won't have to convert 1.000.000+ structs to dictionary I'd use a protocol extension.
Long answer
I created a Mac cmd line app to test the time and as expected the direct conversion is faster.
This is kind of obvious since the compiler could optimize it and since the code itself is simple. When using reflection you create a bunch of extra structures and variables, also add some overhead.
Even though the direct conversion is faster, I think in most real cases would be fine to use the protocol extension approach since it's easy to adopt in your code base. One thing you must have in mind is that your code using reflection is not considering nested objects, this would make the method even slower and also if you used recursion would generate a bigger overhead.
Here are the results for 3 executions encoding 1.000.000 structs to a dictionary:
Build settings
Time for 1000000 executions with S1(direct conversion):
0.569061994552612
Time for 1000000 executions with S2(struct extension + protocol):
7.68360501527786
Time for 1000000 executions with S3(protocol extension):
7.71803396940231
Time for 1000000 executions with S1(direct conversion):
0.500779032707214
Time for 1000000 executions with S2(struct extension + protocol):
7.58478999137878
Time for 1000000 executions with S3(protocol extension):
7.73368299007416
Time for 1000000 executions with S1(direct conversion):
0.492152035236359
Time for 1000000 executions with S2(struct extension + protocol):
7.81585901975632
Time for 1000000 executions with S3(protocol extension):
7.41855001449585
Build settings
Time for 1000000 executions with S1(direct conversion):
2.92627400159836
Time for 1000000 executions with S2(struct extension + protocol):
9.25952398777008
Time for 1000000 executions with S3(protocol extension):
9.19355899095535
Time for 1000000 executions with S1(direct conversion):
2.9830749630928
Time for 1000000 executions with S2(struct extension + protocol):
9.06750500202179
Time for 1000000 executions with S3(protocol extension):
8.77240401506424
Time for 1000000 executions with S1(direct conversion):
2.81389397382736
Time for 1000000 executions with S2(struct extension + protocol):
8.84287703037262
Time for 1000000 executions with S3(protocol extension):
9.08754301071167
Build settings
Time for 1000000 executions with S1(direct conversion):
0.533200979232788
Time for 1000000 executions with S2(struct extension + protocol):
8.15365797281265
Time for 1000000 executions with S3(protocol extension):
7.80043601989746
Time for 1000000 executions with S1(direct conversion):
0.509769976139069
Time for 1000000 executions with S2(struct extension + protocol):
7.76911997795105
Time for 1000000 executions with S3(protocol extension):
8.00845402479172
Time for 1000000 executions with S1(direct conversion):
0.532546997070312
Time for 1000000 executions with S2(struct extension + protocol):
7.99552202224731
Time for 1000000 executions with S3(protocol extension):
7.86273497343063
Build settings
Time for 1000000 executions with S1(direct conversion):
2.90398299694061
Time for 1000000 executions with S2(struct extension + protocol):
9.62662398815155
Time for 1000000 executions with S3(protocol extension):
9.55038601160049
Time for 1000000 executions with S1(direct conversion):
2.98312002420425
Time for 1000000 executions with S2(struct extension + protocol):
9.62088203430176
Time for 1000000 executions with S3(protocol extension):
8.82720899581909
Time for 1000000 executions with S1(direct conversion):
2.77569997310638
Time for 1000000 executions with S2(struct extension + protocol):
8.83749902248383
Time for 1000000 executions with S3(protocol extension):
8.76373296976089
Code:
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)
}