SwiftyJSON性能问题

我遇到严重的性能问题,从我们的APIparsing与SwiftyJson的JSON和填充核心数据。

数据与Alamofire一起下载,很好地工作,但用SwiftyJsonparsingJson是痛苦的缓慢。 为了看看这个库是否真的是这个问题,我重写了parsing数据的许多地方之一的jsonparsing。 在下面的代码中,我parsing了大约400个旅游景点之一的开放时间。

查看这些截图中的差异,7,7秒到185毫秒:

在这里输入图像说明

在这里输入图像说明

Swifty方式:

let openDescription:String = json["OpeningHours"]["OpeningHoursGenericExceptions"].string! let monOpen:[String] = json["OpeningHours"]["Monday"]["From"].string!.componentsSeparatedByString(":") let monClose:[String] = json["OpeningHours"]["Monday"]["To"].string!.componentsSeparatedByString(":") let tueOpen:[String] = json["OpeningHours"]["Tuesday"]["From"].string!.componentsSeparatedByString(":") let tueClose:[String] = json["OpeningHours"]["Tuesday"]["To"].string!.componentsSeparatedByString(":") let wedOpen:[String] = json["OpeningHours"]["Wednesday"]["From"].string!.componentsSeparatedByString(":") let wedClose:[String] = json["OpeningHours"]["Wednesday"]["To"].string!.componentsSeparatedByString(":") let thuOpen:[String] = json["OpeningHours"]["Thursday"]["From"].string!.componentsSeparatedByString(":") let thuClose:[String] = json["OpeningHours"]["Thursday"]["To"].string!.componentsSeparatedByString(":") let friOpen:[String] = json["OpeningHours"]["Friday"]["From"].string!.componentsSeparatedByString(":") let friClose:[String] = json["OpeningHours"]["Friday"]["To"].string!.componentsSeparatedByString(":") let satOpen:[String] = json["OpeningHours"]["Saturday"]["From"].string!.componentsSeparatedByString(":") let satClose:[String] = json["OpeningHours"]["Saturday"]["To"].string!.componentsSeparatedByString(":") let sunOpen:[String] = json["OpeningHours"]["Sunday"]["From"].string!.componentsSeparatedByString(":") let sunClose:[String] = json["OpeningHours"]["Sunday"]["To"].string!.componentsSeparatedByString(":") 

原生的方式:

  var monOpen:[String] = [] var monClose:[String] = [] var tueOpen:[String] = [] var tueClose:[String] = [] var wedOpen:[String] = [] var wedClose:[String] = [] var thuOpen:[String] = [] var thuClose:[String] = [] var friOpen:[String] = [] var friClose:[String] = [] var satOpen:[String] = [] var satClose:[String] = [] var sunOpen:[String] = [] var sunClose:[String] = [] var openDescription:String = "" if let attractionsArray = orgJson as? NSArray{ if let attraction = attractionsArray[0] as? NSDictionary{ if let openHours = attraction["OpeningHours"] as? NSDictionary{ if let day = openHours["Monday"] as? NSDictionary{ if let open = day["From"] as? String{ monOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ monClose = close.componentsSeparatedByString(":") } } if let day = openHours["Tuesday"] as? NSDictionary{ if let open = day["From"] as? String{ tueOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ tueClose = close.componentsSeparatedByString(":") } } if let day = openHours["Wednesday"] as? NSDictionary{ if let open = day["From"] as? String{ wedOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ wedClose = close.componentsSeparatedByString(":") } } if let day = openHours["Thursday"] as? NSDictionary{ if let open = day["From"] as? String{ thuOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ thuClose = close.componentsSeparatedByString(":") } } if let day = openHours["Friday"] as? NSDictionary{ if let open = day["From"] as? String{ friOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ friClose = close.componentsSeparatedByString(":") } } if let day = openHours["Saturday"] as? NSDictionary{ if let open = day["From"] as? String{ satOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ satClose = close.componentsSeparatedByString(":") } } if let day = openHours["Sunday"] as? NSDictionary{ if let open = day["From"] as? String{ sunOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ sunClose = close.componentsSeparatedByString(":") } } if let desc = openHours["OpeningHoursGenericExceptions"] as? String{ openDescription = desc } } } } 

这只是被parsing的数据的一部分,所以性能在应用程序中是非常明显的。

我想问题是,我用SwiftyJSON不正确或这是预期?

首先,你的“本土方式”不等于“Swifty方式”。

SwiftyJSON版本有45个subscript访问,但本地方式只有23个subscript访问。

为了使“本地方式”相当,应该是这样的:

 let openDescription = attraction["OpeningHours"]!["OpeningHoursGenericExceptions"] as String let monOpen = (attraction["OpeningHours"]!["Monday"]!!["From"] as String).componentsSeparatedByString(":") let monClose = (attraction["OpeningHours"]!["Monday"]!!["To"] as String).componentsSeparatedByString(":") let tueOpen = (attraction["OpeningHours"]!["Tuesday"]!!["From"] as String).componentsSeparatedByString(":") let tueClose = (attraction["OpeningHours"]!["Tuesday"]!!["To"]! as String).componentsSeparatedByString(":") // ... 

或“Swifty的方式”应该是这样的:

 let openHours = json[0]["OpeningHours"] var day:JSON day = openHours["Monday"] if let open = day["From"].string { monOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { monClose = close.componentsSeparatedByString(":") } day = openHours["Tuesday"] if let open = day["From"].string { tueOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { tueClose = close.componentsSeparatedByString(":") } // ... 

无论如何,是的,SwiftyJSON很慢。 我们来衡量一下:

 import Foundation import XCTest let orgJson:AnyObject = [ [ "OpeningHours": [ "OpeningHoursGenericExceptions": "test", "Monday": ["From":"1:2:3","To":"1:2:3"], "Tuesday": ["From":"1:2:3","To":"1:2:3"], "Wednesday": ["From":"1:2:3","To":"1:2:3"], "Thursday": ["From":"1:2:3","To":"1:2:3"], "Friday": ["From":"1:2:3","To":"1:2:3"], "Saturday": ["From":"1:2:3","To":"1:2:3"], "Sunday": ["From":"1:2:3","To":"1:2:3"], ] ] ] let json = JSON(orgJson) class JSONTestTests: XCTestCase { func testNativeSubscript() { measureBlock { () -> Void in for _ in 0 ..< 400 { autoreleasepool { if let attraction = orgJson[0] as? NSDictionary { let openDescription = attraction["OpeningHours"]!["OpeningHoursGenericExceptions"] as String let monOpen = (attraction["OpeningHours"]!["Monday"]!!["From"] as String).componentsSeparatedByString(":") let monClose = (attraction["OpeningHours"]!["Monday"]!!["To"] as String).componentsSeparatedByString(":") let tueOpen = (attraction["OpeningHours"]!["Tuesday"]!!["From"] as String).componentsSeparatedByString(":") let tueClose = (attraction["OpeningHours"]!["Tuesday"]!!["To"] as String).componentsSeparatedByString(":") let wedOpen = (attraction["OpeningHours"]!["Wednesday"]!!["From"] as String).componentsSeparatedByString(":") let wedClose = (attraction["OpeningHours"]!["Wednesday"]!!["To"] as String).componentsSeparatedByString(":") let thuOpen = (attraction["OpeningHours"]!["Thursday"]!!["From"] as String).componentsSeparatedByString(":") let thuClose = (attraction["OpeningHours"]!["Thursday"]!!["To"] as String).componentsSeparatedByString(":") let friOpen = (attraction["OpeningHours"]!["Friday"]!!["From"] as String).componentsSeparatedByString(":") let friClose = (attraction["OpeningHours"]!["Friday"]!!["To"] as String).componentsSeparatedByString(":") let satOpen = (attraction["OpeningHours"]!["Saturday"]!!["From"] as String).componentsSeparatedByString(":") let satClose = (attraction["OpeningHours"]!["Saturday"]!!["To"] as String).componentsSeparatedByString(":") let sunOpen = (attraction["OpeningHours"]!["Sunday"]!!["From"] as String).componentsSeparatedByString(":") let sunClose = (attraction["OpeningHours"]!["Sunday"]!!["To"] as String).componentsSeparatedByString(":") XCTAssertEqual(monOpen, ["1","2","3"], "") XCTAssertEqual(openDescription, "test") } } } } } func testJSONSubscript() { measureBlock { () -> Void in for _ in 0 ..< 400 { autoreleasepool { let attraction = json[0] let openDescription:String = attraction["OpeningHours"]["OpeningHoursGenericExceptions"].string! let monOpen:[String] = attraction["OpeningHours"]["Monday"]["From"].string!.componentsSeparatedByString(":") let monClose:[String] = attraction["OpeningHours"]["Monday"]["To"].string!.componentsSeparatedByString(":") let tueOpen:[String] = attraction["OpeningHours"]["Tuesday"]["From"].string!.componentsSeparatedByString(":") let tueClose:[String] = attraction["OpeningHours"]["Tuesday"]["To"].string!.componentsSeparatedByString(":") let wedOpen:[String] = attraction["OpeningHours"]["Wednesday"]["From"].string!.componentsSeparatedByString(":") let wedClose:[String] = attraction["OpeningHours"]["Wednesday"]["To"].string!.componentsSeparatedByString(":") let thuOpen:[String] = attraction["OpeningHours"]["Thursday"]["From"].string!.componentsSeparatedByString(":") let thuClose:[String] = attraction["OpeningHours"]["Thursday"]["To"].string!.componentsSeparatedByString(":") let friOpen:[String] = attraction["OpeningHours"]["Friday"]["From"].string!.componentsSeparatedByString(":") let friClose:[String] = attraction["OpeningHours"]["Friday"]["To"].string!.componentsSeparatedByString(":") let satOpen:[String] = attraction["OpeningHours"]["Saturday"]["From"].string!.componentsSeparatedByString(":") let satClose:[String] = attraction["OpeningHours"]["Saturday"]["To"].string!.componentsSeparatedByString(":") let sunOpen:[String] = attraction["OpeningHours"]["Sunday"]["From"].string!.componentsSeparatedByString(":") let sunClose:[String] = attraction["OpeningHours"]["Sunday"]["To"].string!.componentsSeparatedByString(":") XCTAssertEqual(monOpen, ["1","2","3"], "") } } } } func testNativeBinding() { measureBlock { () -> Void in for _ in 0 ..< 400 { autoreleasepool { var monOpen:[String] = [] var monClose:[String] = [] var tueOpen:[String] = [] var tueClose:[String] = [] var wedOpen:[String] = [] var wedClose:[String] = [] var thuOpen:[String] = [] var thuClose:[String] = [] var friOpen:[String] = [] var friClose:[String] = [] var satOpen:[String] = [] var satClose:[String] = [] var sunOpen:[String] = [] var sunClose:[String] = [] var openDescription:String = "" if let attractionsArray = orgJson as? NSArray{ if let attraction = attractionsArray[0] as? NSDictionary{ if let openHours = attraction["OpeningHours"] as? NSDictionary{ if let day = openHours["Monday"] as? NSDictionary{ if let open = day["From"] as? String{ monOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ monClose = close.componentsSeparatedByString(":") } } if let day = openHours["Tuesday"] as? NSDictionary{ if let open = day["From"] as? String{ tueOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ tueClose = close.componentsSeparatedByString(":") } } if let day = openHours["Wednesday"] as? NSDictionary{ if let open = day["From"] as? String{ wedOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ wedClose = close.componentsSeparatedByString(":") } } if let day = openHours["Thursday"] as? NSDictionary{ if let open = day["From"] as? String{ thuOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ thuClose = close.componentsSeparatedByString(":") } } if let day = openHours["Friday"] as? NSDictionary{ if let open = day["From"] as? String{ friOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ friClose = close.componentsSeparatedByString(":") } } if let day = openHours["Saturday"] as? NSDictionary{ if let open = day["From"] as? String{ satOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ satClose = close.componentsSeparatedByString(":") } } if let day = openHours["Sunday"] as? NSDictionary{ if let open = day["From"] as? String{ sunOpen = open.componentsSeparatedByString(":") } if let close = day["To"] as? String{ sunClose = close.componentsSeparatedByString(":") } } if let desc = openHours["OpeningHoursGenericExceptions"] as? String{ openDescription = desc } } } } XCTAssertEqual(monOpen, ["1","2","3"], "") } } } } func testJSONBinding() { measureBlock { () -> Void in for _ in 0 ..< 400 { autoreleasepool { var monOpen:[String] = [] var monClose:[String] = [] var tueOpen:[String] = [] var tueClose:[String] = [] var wedOpen:[String] = [] var wedClose:[String] = [] var thuOpen:[String] = [] var thuClose:[String] = [] var friOpen:[String] = [] var friClose:[String] = [] var satOpen:[String] = [] var satClose:[String] = [] var sunOpen:[String] = [] var sunClose:[String] = [] var openDescription:String = "" let openHours = json[0]["OpeningHours"] var day:JSON day = openHours["Monday"] if let open = day["From"].string { monOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { monClose = close.componentsSeparatedByString(":") } day = openHours["Tuesday"] if let open = day["From"].string { tueOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { tueClose = close.componentsSeparatedByString(":") } day = openHours["WednesDay"] if let open = day["From"].string { wedOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { wedClose = close.componentsSeparatedByString(":") } day = openHours["Thursday"] if let open = day["From"].string { thuOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { thuClose = close.componentsSeparatedByString(":") } day = openHours["Friday"] if let open = day["From"].string { friOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { friClose = close.componentsSeparatedByString(":") } day = openHours["Saturday"] if let open = day["From"].string { satOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { satClose = close.componentsSeparatedByString(":") } day = openHours["Sunday"] if let open = day["From"].string { sunOpen = open.componentsSeparatedByString(":") } if let close = day["To"].string { sunClose = close.componentsSeparatedByString(":") } XCTAssertEqual(monOpen, ["1","2","3"], "") } } } } } 

输出:

 <unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testJSONBinding]' measured [Time, seconds] average: 0.804, relative standard deviation: 5.592%, values: [0.835687, 0.814827, 0.819685, 0.841900, 0.764961, 0.845202, 0.691442, 0.779255, 0.818213, 0.830698], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 <unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testJSONSubscript]' measured [Time, seconds] average: 4.247, relative standard deviation: 3.496%, values: [4.019640, 4.004123, 4.146146, 4.194535, 4.487171, 4.300971, 4.310613, 4.408405, 4.318354, 4.279362], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 <unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testNativeBinding]' measured [Time, seconds] average: 0.223, relative standard deviation: 2.773%, values: [0.221099, 0.227395, 0.218860, 0.225989, 0.227128, 0.222370, 0.229956, 0.214535, 0.210818, 0.229868], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 <unknown>:0: Test Case '-[JSONTestTests.JSONTestTests testNativeSubscript]' measured [Time, seconds] average: 0.362, relative standard deviation: 17.528%, values: [0.346285, 0.316185, 0.333650, 0.339416, 0.330243, 0.354034, 0.378730, 0.269519, 0.486904, 0.467607], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 
  • 你的SwiftyJSON:4.247
  • 你的母语:0.223
  • 我的SwiftyJSON:0.804
  • 我的母语:0.362

顺便说一句,如果我是你,我会做这样的事情:

 if let hours = orgJson[0]?["OpeningHours"] as? NSDictionary { let openDescription = hours["OpeningHoursGenericExceptions"] as? String ?? "" let monOpen = hours["Monday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let monClose = hours["Monday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let tueOpen = hours["Tuesday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let tueClose = hours["Tuesday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let wedOpen = hours["Wednesday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let wedClose = hours["Wednesday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let thuOpen = hours["Thursday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let thuClose = hours["Thursday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let friOpen = hours["Friday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let friClose = hours["Friday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let satOpen = hours["Saturday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let satClose = hours["Saturday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] let sunOpen = hours["Sunday"]?["From"]??.componentsSeparatedByString?(":") as? [String] ?? [] let sunClose = hours["Sunday"]?["To"]??.componentsSeparatedByString?(":") as? [String] ?? [] // ... } 

这是相当快,安全,不那么复杂。


为什么SwiftyJSON速度慢?

在下subscript访问上,SwiftyJSON会:

  1. 检查的关键是String
  2. 再次用self[key:sub]下标self[key:sub]
  3. 检查底层对象是NSDictionary
  4. 下标与提供的密钥NSDictionary
  5. 用结果构造JSON对象
  6. return

也许编译器优化了一些步骤,但“慢于本地”是有点不可避免的:)