swift_dynamiccast 处理过多并影响 iOS 中的性能
Posted
技术标签:
【中文标题】swift_dynamiccast 处理过多并影响 iOS 中的性能【英文标题】:swift_dynamiccast taking too much in processing and affecting performance in iOS 【发布时间】:2017-05-11 02:58:40 【问题描述】:我在解析和创建来自服务器的 json 数据的一些组合方面进行了大量计算。整个过程需要很多时间,主要是我已经修复了与代码相关的问题,但是时间分析器在一个地方显示了一个我无法弄清楚的特定调用所花费的时间。
在我的处理过程中,我进行了大量的选角。它创建了很多 FlightFare 类型的对象,我从字典中创建它。
所以conveninece init 如下所示,我怎样才能避免它..?
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]
【问题讨论】:
使用 [String: AnyObject] 而不是 [String: Any] 的任何特殊原因? AnyObject 确实在我在这里做的一些测试中增加了一些开销。 @MarcoPompei 没有理由,在 swift 2.3 中使用它,所以保持这种方式。我会试试 Any 如果对象的属性是可选的,那么if let...
构造是不必要的开销。只需将条件向下转换直接分配给属性
【参考方案1】:
听起来像是在做value as? String
之类的事情时缴纳的税款。您将该节点折叠在配置文件中,因此我只能给出避免这种情况的一般建议:确保您不会重复处理原始形式的相同数据。作为迁移到类型化中间形式的一部分,强制转换一次。
最小化类型转换是各种 Swift JSON 库(如 Freddy 和 SwiftyJSON)在解析 JSON 时试图保证的事情。
如果您的 JSON 数据的结构同样不确定 - 此节点是字符串还是对象,还是只是 null? - 那么你的代码也会遇到并且必须在那个级别解决这个问题。
ETA:如果演员表的成本明显计入您的便利 init(您应该使用配置文件验证这一点),那么该方法确实是一个问题。您可以通过使用将 JSON 解析为类型表示的库来避免这种情况,例如前面提到的 Freddy。这用库中的枚举大小写匹配替换了代码中的强制转换。
选中配置文件选项中告诉它不向您显示框架代码的框通常很有用,因此您可以轻松查看您直接控制的哪些方法需要时间。然后,您可以深入了解配置文件以获取每行成本统计信息以集中优化。
【讨论】:
我在问题中添加了更多细节。为什么投射一个对象会花费这么多时间......? @AnkitSrivastava 通常不是因为它需要很多时间,而是因为你做了很多事情,以至于即使它花费的时间很短也会变得引人注目。类似地,Obj-C 程序有时可以描述为在高度优化的消息发送例程中花费大量时间,不是因为这非常慢,而是因为消息量巨大。【参考方案2】:您可以稍微不同地编写 init() 来分离展开的表单类型转换并获得大约 40% 的速度
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
或者,如果字典包含 NSNumbers、NSStrings 等,并且您愿意为您的内部变量使用“NS”类型,那么将没有“动态”转换,您将获得 13 倍的性能提升那部分:
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
您可以使用的另一种策略是将字典存储在对象中而不分配变量并使它们懒惰地初始化它们的值:
例如:
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
这只有在 init() 出于某些其他原因没有引用这些变量的情况下才会起作用,但是它应该使对象创建时间几乎为零,并通过仅在实际使用时执行它们来延迟/扩展类型转换时间数据。
【讨论】:
谢谢...我会试试这些。【参考方案3】:首先,在 Swift 中使用 [String: Any]
而不是 [String: AnyObject]
作为字典。
尝试使用SwiftyJSON
SwiftyJSON 使在 Swift 中处理 JSON 数据变得容易。
PS:有这么多 if-else 条件并不是最佳实践。
【讨论】:
SwiftyJSON 使程序员更容易,但它通过将 NSJSONSerialization 的返回值包装在它自己的结构树中来做到这一点,并且你会因此而受到性能影响。如果问题是运行时速度不够快,添加 SwiftyJSON 只会让情况变得更糟。【参考方案4】:您不应该在分配之前检查值。
如果所有这些值都是可选的,您可以简单地分配可选值score = dictionary["r"] as? Double
,如果该键没有值,它将简单地将其分配为 nil。
如果你想让变量有一个默认值,你可以 nil 合并值score = dictionary["r"] as? Double ?? 0
这将使您必须访问字典的次数减少一半,并减少临时变量的分配。
【讨论】:
以上是关于swift_dynamiccast 处理过多并影响 iOS 中的性能的主要内容,如果未能解决你的问题,请参考以下文章