Leaked NSURLConnection in Swift

I have found several other articles on memory issues with NSURLConnection, however none of them are related to projects using ARC. My problem is I do NSURLConnections every 0.5s (yes, I know it would be better with sockets, but I have no control over the API). When I open the xCode Debug Inspector, I could clearly see my used memory growing:

enter image description here

When I view my application with tools, however - no leaks, persistent storage across generations of labels, just nothing:

enter image description here

I deliberately decoupled all my other logic and created a simple application to show my problem.

This is the main (and only) ViewController

import UIKit

class ViewController: UIViewController {

    private var timer: NSTimer!

    private var requests = [ServerRequest]()

    override func viewDidAppear(animated: Bool)
    {
        self.timer = NSTimer(timeInterval: 0.5, target: self, selector: "refresh", userInfo: nil, repeats: true)
        NSRunLoop.mainRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes)
    }

    @objc private func refresh()
    {
        let urlRequest = NSURLRequest(URL: NSURL(string: "https://api.stackexchange.com/2.2/questions?order=desc&sort=activity&site=stackoverflow")!)

        let serverRequest = ServerRequest(request: urlRequest){
            [unowned self] result, error, request in

            println("yaba daba duu")

            self.requests.removeAll(keepCapacity: false)
        }

        requests.append(serverRequest)

        serverRequest.send()
    }
}

      

And here is my asynchronous connection delegate:

import Foundation

class ServerRequest: NSObject
{
    private var request: NSURLRequest!
    typealias completionType = (NSData?, NSError?, ServerRequest) -> ()
    private var completion: completionType!
    private var receivedData: NSMutableData?
    private var connection: NSURLConnection?

    init(request: NSURLRequest, completion: (NSData?, NSError?, ServerRequest) -> ())
    {
        super.init()

        self.request = request
        self.completion = completion
        self.connection = NSURLConnection(request: self.request, delegate: self, startImmediately:false)
        //this line will help connection is fired even while tere are touch events
        self.connection?.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
        NSURLCache.sharedURLCache().removeAllCachedResponses()
    }

    func send()
    {
        receivedData = NSMutableData()

        self.connection?.start()
    }

    func abort()
    {
        self.connection?.cancel()
    }
}

extension ServerRequest: NSURLConnectionDataDelegate, NSURLConnectionDelegate
{
    func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse)
    {
        self.receivedData?.length = 0
    }

    func connection(connection: NSURLConnection, didReceiveData data: NSData)
    {
        self.receivedData?.appendData(data)
    }

    func connection(connection: NSURLConnection, didFailWithError error: NSError)
    {
        self.connection = nil
        self.completion(nil, error, self)
    }

    func connectionDidFinishLoading(connection: NSURLConnection)
    {
        self.connection = nil
        self.completion(receivedData, nil, self)
    }

    //MARK: https specific(canAuthenticateAgainstProtectionSpace is depraceted first in iOS 8)
    func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool
    {
        return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
    }

    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
    {
        challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
        challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)
    }

    func connection(connection: NSURLConnection, willCacheResponse cachedResponse: NSCachedURLResponse) -> NSCachedURLResponse? {
        return nil
    }

}

      

Please note that I need to connect to the https server, so I need the appropriate delegation methods. Also, I have implemented caching methods due to some suggestions in SO that caching responses in NSURLConnection might cause my problems. The final request is scheduled in "NSRunLoopCommonModes" because I need it while it is scrolling (although removing that line does not affect the problem anyway).

UPDATE

I tried even without a custom class with a delegate and the result is almost the same. Just copy the following link:

import UIKit

class ViewController: UIViewController {

    private var timer: NSTimer!

    override func viewDidAppear(animated: Bool)
    {
        self.timer = NSTimer(timeInterval: 0.5, target: self, selector: "refresh", userInfo: nil, repeats: true)
        NSRunLoop.mainRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes)
    }

    @objc private func refresh()
    {
        let urlRequest = NSURLRequest(URL: NSURL(string: "https://api.stackexchange.com/2.2/questions?order=desc&sort=activity&site=stackoverflow")!)

        NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue())
        {
            response, data, error in

            println("yaba daba duu")

        }
    }
}

      

And my memory pressure rises again:

enter image description here

+3


source to share





All Articles