I am doing a lot of computing in parsing and creating some combination from json data coming from server. The entire process takes a lot of time and majorly I have fixed code related issue but at one place time profiler shows the time taken by one particular call which I cannot figure out.
I have a lot of casting going on in my processing. It creates a lot of objects of type FlightFare and I create it from a dictionary.
So the conveninece init looks like below, how can I avoid it..?
convenience init (dictionary: [String:AnyObject]) {
self.init()
refundType = dictionary["rt"] as! String
if let unwrappedScore = dictionary["r"] as? Double {
score = unwrappedScore
}
if let unwrappedValue = dictionary["t"] as? Int {
taxes = unwrappedValue
}
if let unwrappedValue = dictionary["bf"] as? Int {
baseFare = unwrappedValue
}
if let unwrappedValue = dictionary["f"] as? Int {
fee = unwrappedValue
}
if let unwrappedValue = dictionary["d"] as? Int {
discount = unwrappedValue
}
if let unwrappedValue = dictionary["tf"] as? Int {
fare = unwrappedValue
}
if let unwrappedValue = dictionary["ttf"] as? Int {
totalFare = unwrappedValue
}
if let unwrappedValue = dictionary["hbo"] as? Bool {
hbo = unwrappedValue
}
providerKey = dictionary["pk"] as? String
hbf = dictionary["hbf"] as? [String]
}
Sounds like the tax paid when doing things like value as? String
. You left that node collapsed in the profile, so I can only give general advice on avoiding this: Ensure you aren't repeatedly processing the same data in its raw form. Cast once as part of migrating to a typed intermediate form.
Minimizing type casts is something that the various Swift JSON libraries, like Freddy and SwiftyJSON, attempt to guarantee in their parsing of JSON.
If the structure of your JSON data is similarly uncertain - is this node a string or an object or just null? - then your code runs into and must address this issue at that level, too.
ETA: If the cost of the casts is noticeably charged to your convenience init (you should verify this using the profile), then that method would indeed be a problem. You can avoid it by using a library that parses JSON into a typed representation to begin with, such as Freddy mentioned earlier. This replaces casting in your code with enum case matching within the library.
It's often useful to check the box in the profile options that tells it not to show you framework code, so you can easily see which of your methods that you directly control is taking time. Then you can drill in in the profile to get per-line cost statistics to focus your optimization.
You could write the init() a little differently to separate unwrapping form type casting and gain around 40% speed
for (key,value) in dictionary
{
switch key
{
case "rt" : refundType = value as! String
case "r" : score = value as! Double
case "bf" : baseFare = value as! Int
case "f" : fee = value as! Int
case "d" : discount = value as! Int
case "tf" : fare = value as! Int
case "ttf" : totalFare = value as! Int
case "hbo" : hbo = value as! Bool
case "pk" : providerKey = value as! String
case "hbf" : hbf = value as! [String]
default : break
}
}
Or, if the dictionary contains NSNumbers, NSStrings, etc. and you're willing to work with the "NS" types for your internal variable, then there would be no "Dynamic" casting and you'd get a 13x performance boost for that part:
for (key,value) in dictionary
{
switch key
{
case "rt" : refundType = value as! NSString
case "r" : score = value as! NSNumber
case "bf" : baseFare = value as! NSNumber
case "f" : fee = value as! NSNumber
case "d" : discount = value as! NSNumber
case "tf" : fare = value as! NSNumber
case "ttf" : totalFare = value as! NSNumber
case "hbo" : hbo = value as! NSNumber
case "pk" : providerKey = value as! NSString
case "hbf" : hbf = value as! NSArray
default : break
}
}
Another strategy you could use is to store the dictionary inside the object without assigning the variables and make them lazy init their values:
For example:
var defaultDict:[String:AnyObject] = [:]
lazy var score:Double = self.defaultDict["r"] as? Double ?? 0
lazy var baseFare:Int = self.defaultDict["bf"] as? Int ?? 0
...
convenience init (dictionary:[String:AnyObject])
{
self.init()
defaultDict = dictionary
}
This would only wok if the init() doesn't reference these variables for some other reasons but, it should bring the object creation time to almost nothing and defer/spread the type cast times by only performing them on actual use of the data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With