调试 NSJSONSerialization 内存泄漏
Debugging NSJSONSerialization memory leaks
我在 Swift 2.0 项目中使用 SwiftyJSON 2.3.0 进行 JSON 解析:
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 进行世代分析,结果如下:
似乎 NSJSONSerialization.JSONObjectWithData
是原因。然而,泄漏的内存块是 malloc_zone_malloc
/malloc_zone_calloc
(即不是 ARC-managed),所以我看不到引用计数。
如何调试这个问题?
此问题的起因是 Swift 2.0 错误(参见 Xcode 7 release notes):
"对具有 as 模式的多个类型使用 switch 可能会导致内存泄漏。例如,避免这种 switch 语句:
switch x {
case let a as A: ...
case let b as B: ...
case let c as C: ...
default: ...
}
重写代码以使用 if let a = x as?一个语句而不是开关。此模式执行类型检查以避免内存泄漏。 (22587077)
顺便说一下,这正是 SwiftyJSON 在内部使用的 switch 语句类型。有一个未解决的问题:https://github.com/SwiftyJSON/SwiftyJSON/issues/323
此错误已在 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"])
}
(归功于 tehong)
我在 Swift 2.0 项目中使用 SwiftyJSON 2.3.0 进行 JSON 解析:
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 进行世代分析,结果如下:
似乎 NSJSONSerialization.JSONObjectWithData
是原因。然而,泄漏的内存块是 malloc_zone_malloc
/malloc_zone_calloc
(即不是 ARC-managed),所以我看不到引用计数。
如何调试这个问题?
此问题的起因是 Swift 2.0 错误(参见 Xcode 7 release notes):
"对具有 as 模式的多个类型使用 switch 可能会导致内存泄漏。例如,避免这种 switch 语句:
switch x {
case let a as A: ...
case let b as B: ...
case let c as C: ...
default: ...
}
重写代码以使用 if let a = x as?一个语句而不是开关。此模式执行类型检查以避免内存泄漏。 (22587077)
顺便说一下,这正是 SwiftyJSON 在内部使用的 switch 语句类型。有一个未解决的问题:https://github.com/SwiftyJSON/SwiftyJSON/issues/323
此错误已在 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"])
}
(归功于 tehong)