2015-03-25 219 views
13

我遇到嚴重的性能問題,從我們的API解析JSON與SwiftyJson並填充核心數據。SwiftyJSON性能問題

數據與Alamofire一起下載,這很好地工作,但用SwiftyJson解析json是痛苦的緩慢。爲了看看這個庫是否真的存在問題,我重寫了解析數據的許多地方之一的json解析。在下面的代碼中,我解析了大約400個旅遊景點之一的開放時間。

看到這些截圖的差異,7,7秒到185毫秒:

enter image description here

enter image description here

的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 
       } 
      } 
     } 
    } 

這只是要解析的數據的一部分,因此應用程序中的性能非常顯着。

我想問題是,我用SwiftyJSON不正確還是這是預期?

回答

20

首先,你的「本土方式」不等於「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]
  3. 檢查底層對象是NSDictionary
  4. 下標與所提供的密鑰
  5. 底層 NSDictionary
  6. 構造JSON對象與結果
  7. return

也許編譯器優化了一些措施,但「比本地慢」是有點不可避免:)

+0

感謝您一個非常完整的答案!並與基準!因爲我在Swift上很新穎(我猜很多人都是:)很多編碼是first-try-trial-and-error,我按照SwiftyJSON自述文件中的建議進行了JSON解析。看看解析方法的不同例子,我並不後悔從我的項目中取出swifty,「本地」解析的複雜性並不是真正的問題,並且增加了性能差異,我沒有看到swifty作爲選項。 – Simpa 2015-03-29 08:15:06

+0

我的意思是Swifty的創建者沒有冒犯,我認爲它可以對很多開發者有很大的用處。但是使用google快速解析json幾乎只給出了使用swifty的結果給我的印象是要走的路。但也許這不適合每個人? :) – Simpa 2015-03-29 08:23:33

+0

@Simpa在Swift世界中,谷歌中的很多點擊是不可信的。人們會得到Objective-C已經死亡的印象,Cocoapods被接受爲標準,等等......事實上,我認爲很多這些外觀都是有問題的。但是,SwiftyJSON使用起來太慢了?這是另一個問題,但如果你最初得到了這些結果,你可能沒有注意到。 – 2016-02-17 23:54:24