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] }
4 Answers
Answers 1
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.
Answers 2
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.
Answers 3
First, use [String: Any]
instead of [String: AnyObject]
for dictionary in Swift.
Try using SwiftyJSON
SwiftyJSON makes it easy to deal with JSON data in Swift.
PS: Having so many if-else conditions is not a best practise.
Answers 4
You should not be checking for a value before assigning.
If all of those values are optional, you can simply assign the optional value score = dictionary["r"] as? Double
and if there is no value for that key, it will simply assign it as nil.
If you want the variables to have a default value, you can nil coalesce the value score = dictionary["r"] as? Double ?? 0
This will cut the number of times you have to access the dictionary in half and also reduce the assignment of temporary variables.
0 comments:
Post a Comment