[swift]调试 NSJSONSerialization 内存泄漏

标签: json ios Swift
发布时间: 2017/3/5 13:10:23
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我使用SwiftyJSON 2.3.0 JSON 解析在我 Swift 2.0 的项目︰

extension NSData {
    func JSONDict() -> Dictionary<String, AnyObject>? {
        do {
            return try NSJSONSerialization.JSONObjectWithData(self, options: NSJSONReadingOptions(rawValue: 0)) as? Dictionary<String, AnyObject>
        } catch let error as NSError {
            DLog("NSData: error converting data to JSON: \n\(self.asString)\nerror: \n\(error)")
            return nil
        }
    }

}

class NetworkOperation {

    lazy var defaultCompletionHandler: NSURLSessionCompletionHandler = { [weak self] (data, response, error) in
        guard let s = self where !s.cancelled else {
            return
        }

        if let httpResponse = response as? NSHTTPURLResponse {
            switch httpResponse.statusCode {
                case 200..<300:
                    s.successResponse(data, response: response, error: error)

                // ...
            }
        } else {
            // ...
        }            
    }

    func successResponse(data: NSData!, response: NSURLResponse!, error: NSError!) {
        DLog("NetworkOperation: '\(name!)' finished")

        if let jsonDict = data.JSONDict() {
            let json = JSON(jsonDict)
            // process JSON
        }
    }
}

一切工作正常,但我的应用程序似乎泄漏大量的内存,每次它解析 JSON。我试着做代分析使用仪器,和在这里,因为它的显示︰

Instruments screenshot

它看起来像 NSJSONSerialization.JSONObjectWithData 是的原因。泄露大块的内存,然而,是 malloc_zone_malloc / malloc_zone_calloc (即不ARC管理),所以我看不见的引用计数。

如何着手调试这个问题?

解决方法 1:

此问题的原因是 Swift 2.0 bug (见 Xcode 7发行说明)︰

"反对与作为模式的多个类型的使用开关可能会导致内存泄漏。例如,避免这种 switch 语句︰

switch x {
  case let a as A: ...
  case let b as B: ...
  case let c as C: ...
  default: ...
}

重写代码以使用如果让 = x?而不是开关语句。这种模式执行类型检查,避免内存泄漏。(22587077)"

顺便说一下,这是完全的 SwiftyJSON 内部使用 switch 语句类型。有是一个悬而未决的问题︰ https://github.com/SwiftyJSON/SwiftyJSON/issues/323

此 bug 已修复在 Swift 2.1 (根据 @zaph)。现在,解决办法是更换中的以下代码 SwiftyJSON.swift :

switch newValue {
    case let number as NSNumber:
        if number.isBool {
            _type = .Bool
        } else {
            _type = .Number
        }
        self.rawNumber = number
    case  let string as String:
        _type = .String
        self.rawString = string
    case  _ as NSNull:
        _type = .Null
    case let array as [AnyObject]:
        _type = .Array
        self.rawArray = array
    case let dictionary as [String : AnyObject]:
        _type = .Dictionary
        self.rawDictionary = dictionary
    default:
        _type = .Unknown
        _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
}

与︰

if let number = newValue as? NSNumber {
    if number.isBool {
        _type = .Bool
    } else {
        _type = .Number
    }
    self.rawNumber = number
} else if let string = newValue as? String {
    _type = .String
    self.rawString = string
} else if let _ = newValue as? NSNull {
    _type = .Null
} else if let array = newValue as? [AnyObject] {
    _type = .Array
    self.rawArray = array
} else if let dictionary = newValue as? [String : AnyObject] {
    _type = .Dictionary
    self.rawDictionary = dictionary
} else {
    _type = .Unknown
    _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
}

(归功于

官方微信
官方QQ群
31647020