jeudi 26 novembre 2015

How to solve Swift memory leaks when type casting/checking values from reflection?

I created a Serializable Protocol that turns a object in a [String:AnyObject] object (JSON format) using reflection, but I'm getting a leak warning when type casting/checking in Swift 2.1.

I already knows that Swift has some memory leaks problems, as related in the SwifityJSON project (link: http://ift.tt/1MF3ksh), but I couldn't get my code to work without the leaks.

I'm using Xcode 7.1.1 (7B1005) and Swift 2.1, my deployment target is 8.0 and the app is running in the Iphone 5 simulator.

Someone knows any workaround or solution to this? Maybe I'm doing something wrong. My code: (just add the code to a project and call test function with leaks profile):

public class testB: Serializable {

    let a:Int
    let b:Int?

    public init() {
        a = 1
        b = nil
    }

}

public class testA : Serializable {

    let a:String
    let b:Double
    var c:[String:Serializable]
    var d:[Double]
    var e:[String:String]

    public init() {
        a = "teste"
        b = 1.2
        c = [String:Serializable]()
        c["teste"] = testB()
        d = [Double()]
        d[0] = 3.5
        e = [String:String]()
        e["teste"] = "teste"
    }    
}


public func test() {
    testA().toDictionary()
}

public protocol Serializable {

}

extension Serializable {

    func getValue(unknownValue:Any) -> Any? {

        let mi = Mirror(reflecting: unknownValue)
        if mi.displayStyle != .Optional {
            return unknownValue
        }

        if mi.children.count == 0 {
            return nil
        }

        let (_, some) = mi.children.first!
        return some
    }

    //Memory Leak happens here.
    func toDictionary() -> [String:AnyObject] {

        var result = [String:AnyObject]()

        let serializableMirror = Mirror(reflecting: self)

            for childMirror in serializableMirror.children {

                let label = childMirror.label

                if let value = self.getValue(childMirror.value) {

                    if let x = value as? NSData {
                        result[label!] = x.base64EncodedDataWithOptions(.Encoding64CharacterLineLength)

                    } else if let x = value as? NSDate {
                        result[label!] = x.timeIntervalSince1970

                    } else if let x = value as? Serializable {
                        result[label!] = x.toDictionary()

                    } else if let x = value as? [NSData] {
                        var newArray = [NSData]()
                        for xItem in x {
                            newArray.append(xItem.base64EncodedDataWithOptions(.Encoding64CharacterLineLength))
                        }
                        result[label!] = newArray

                    } else if let x = value as? [NSDate] {
                        var newArray = [NSTimeInterval]()
                        for xItem in x {
                            newArray.append(xItem.timeIntervalSince1970)
                        }
                        result[label!] = newArray

                    } else if let x = value as? [Serializable] {
                        var newArray = [[String:AnyObject]]()
                        for xItem in x {
                            newArray.append(xItem.toDictionary())
                        }
                        result[label!] = newArray

                    } else if let x = value as? [String:NSData] {
                        var newDictionary = [String:NSData]()
                        for (y,z) in x {
                            newDictionary[y] = z.base64EncodedDataWithOptions(.Encoding64CharacterLineLength)
                        }
                        result[label!] = newDictionary

                    } else if let x = value as? [String:NSDate] {
                        var newDictionary = [String:NSTimeInterval]()
                        for (y,z) in x {
                            newDictionary[y] = z.timeIntervalSince1970
                        }
                        result[label!] = newDictionary

                    } else if let x = value as? [String:Serializable] {
                        var newDictionary = [String:[String:AnyObject]]()
                        for (y,z) in x {
                            newDictionary[y] = z.toDictionary()
                        }
                        result[label!] = newDictionary

                    } else if value is NSNumber || value is String
                        || value is [Bool] || value is [Int] || value is [Double] || value is [Float] || value is [String]
                        || value is [String:Bool] || value is [String:Int] || value is [String:Double] || value is [String:Float] || value is [String:String] {
                        if let x = value as? AnyObject {
                            result[label!] = x
                        }
                    }
            }
        }
        return result
    }
}

The leaks message.

enter image description here





Aucun commentaire:

Enregistrer un commentaire