Swift_dynamiccast слишком много обрабатывает и влияет на производительность в iOS

Я занимаюсь множеством вычислений при разборе и создаю некоторую комбинацию из json-данных, поступающих с сервера. Весь процесс занимает много времени, и, главным образом, у меня проблема с фиксированным кодом, но в одном месте профилировщик времени показывает время, затраченное на один конкретный вызов, который я не могу понять. введите описание изображения здесь

У меня много кастингов в моей обработке. Он создает множество объектов типа FlightFare, и я создаю его из словаря.

Итак, convinece 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]
}

Ответ 1

Звучит как налог, уплачиваемый при выполнении таких вещей, как value as? String. Вы ушли от того, что node рухнул в профиле, поэтому я могу дать только общий совет об этом: убедитесь, что вы не многократно обрабатываете одни и те же данные в своей необработанной форме. Бросьте один раз как часть перехода к типизированной промежуточной форме.

Сведение к минимуму типов - это то, что различные библиотеки Swift JSON, такие как Freddy и SwiftyJSON, пытаются гарантировать их разбор JSON.

Если структура ваших данных JSON аналогична неопределенной - это node строка или объект или просто нуль? - тогда ваш код столкнется и должен решить эту проблему на этом уровне.

ETA: Если стоимость закладок заметно заряжается до вашего удобства init (вы должны проверить это с помощью профиля), тогда этот метод действительно будет проблемой. Вы можете избежать этого, используя библиотеку, которая сначала анализирует JSON в типизированном представлении, например, упомянутый ранее Freddy. Это заменяет кастинг в вашем коде с совпадением совпадений в библиотеке.

Часто бывает полезно установить флажок в параметрах профиля, который говорит ему, чтобы он не показывал вам код фреймворка, поэтому вы можете легко увидеть, какие из ваших методов, которые вы контролируете напрямую, занимают время. Затем вы можете просверлить профиль, чтобы получить статистику затрат на линию, чтобы сосредоточить свою оптимизацию.

Ответ 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

Сначала используйте [String: Any] вместо [String: AnyObject] для словаря в Swift.

Попробуйте использовать SwiftyJSON

SwiftyJSON упрощает работу с данными JSON в Swift.

PS: Наличие стольких условий if-else не является лучшей практикой.

Ответ 4

Вы не должны проверять значение перед назначением.

Если все эти значения являются необязательными, вы можете просто назначить необязательное значение score = dictionary["r"] as? Double, и если для этого ключа нет значения, он просто назначит его как nil.

Если вы хотите, чтобы переменные имели значение по умолчанию, вы можете nil объединить значение score = dictionary["r"] as? Double ?? 0

Это сократит количество раз, когда вам потребуется доступ к словарю пополам, а также уменьшите назначение временных переменных.