我正在使用我们的API从
SwiftyJson解析JSON并填充核心数据来严重执行性能问题.
数据是与Alamofire下载的,它很好地工作,但用SwiftyJson解析json是非常慢的.为了看看图书馆真的是这个问题,我重写了在解析数据的许多地方之一的json解析.在下面的代码中,我解析了约400个旅游景点之一的开放时间.
看到这些截图的差异,7,7秒到185毫秒:
快乐的方式:
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 } } } }
这只是要解析的数据的一部分,因此该应用程序中的性能非常明显.
我猜问题是,我使用SwiftyJSON不正确或者这是预期的吗?
解决方法
首先,你的“本土方式”不等同于“Swifty方式”.
SwiftyJSON版本有45个下标访问,但本机只有23个下标访问.
要使“本土方式”相当,应该是这样的:
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 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","Wednesday": ["From":"1:2:3","Thursday": ["From":"1:2:3","Friday": ["From":"1:2:3","Saturday": ["From":"1:2:3","Sunday": ["From":"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,"") } } } } 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,"") } } } } 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,"") } } } } }
输出:
<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],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],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],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慢?
在下标访问中,SwiftyJSON确实:
>检查键是String
>下标再次与self [key:sub]
>检查底层对象是NSDictionary
>下标底层的NSDictionary与提供的键
>使用结果构造JSON对象
>返回
也许编译器优化了一些步骤,但是“比本机慢”有些不可避免的:)