2017-02-19 11 views
0

AppleのInstrumentsツールを使用して、アプリケーションの現在の進行状況をチェックし、早急にリークを管理しています。私は多くのリークのを持っているようですが、どこから来ているのか分かりません。私の421のメモリリークはどこから来ますか?

私のアプリケーションでは、OperationのサブクラスであるSignInOperationがあります。また、それはURLSessionDataDelegateに準拠しているので、補完ハンドラを使用せずに自分の要求を処理できます。たとえば、インスタンスにインスタンスを追加する場合、UIの更新を実行する操作では、SignInOperationインスタンスが依存関係になるため、SignInOperationerrorおよびuserプロパティをチェックしてUIアップデートを処理できます。

クラスは、次のとおりです。

その後
import UIKit 

/// Manages a sign-in operation. 
internal final class SignInOperation: Operation, URLSessionDataDelegate { 

    // MARK: - Properties 

    /// An internal flag that indicates whether the operation is currently executing. 
    private var _executing = false 

    /// An internal flag that indicates wheterh the operation is finished. 
    private var _finished = false 

    /// The received data from the operation. 
    private var receivedData = Data() 

    /// The data task used for sign-in. 
    private var sessionTask: URLSessionDataTask? 

    /// The URL session that is used for coordinating tasks used for sign-in. 
    private var localURLSession: URLSession { return URLSession(configuration: localConfiguration, delegate: self, delegateQueue: nil) } 

    /// The configuration used for configuring the URL session used for sign-in. 
    private var localConfiguration: URLSessionConfiguration { return .ephemeral } 

    /// The credentials used for user-sign-in. 
    private var credentials: UserCredentials 

    /// The current user. 
    internal var currentUser: User? 

    /// The error encountered while attempting sign-in. 
    internal var error: NetworkRequestError? 

    /// The cookie storage used for persisting an authentication cookie. 
    internal var cookieStorage: HTTPCookieStorage? 

    /// A Boolean value indicating whether the operation is currently executing. 
    override internal(set) var isExecuting: Bool { 
     get { return _executing } 
     set { 
      willChangeValue(forKey: "isExecuting") 
      _executing = newValue 
      didChangeValue(forKey: "isExecuting") 
     } 
    } 

    /// A Boolean value indicating whether the operation has finished executing its task. 
    override internal(set) var isFinished: Bool { 
     get { return _finished } 
     set { 
      willChangeValue(forKey: "isFinished") 
      _finished = newValue 
      didChangeValue(forKey: "isFinished") 
     } 
    } 

    /// A Boolean value indicating whether the operation executes its task asynchronously. 
    override var isAsynchronous: Bool { return true } 

    // MARK: - Initialization 

    /// Returns an instane of `SignInOperation`. 
    /// - parameter credentials: The credentials for user-sign-in. 
    init(credentials: UserCredentials, cookieStorage: HTTPCookieStorage = CookieStorage.defaultStorage) { 
     self.credentials = credentials 
     self.cookieStorage = cookieStorage 
     super.init() 
     localURLSession.configuration.httpCookieAcceptPolicy = .never 
    } 

    // MARK: - Operation Lifecycle 

    override func start() { 
     if isCancelled { 
      isFinished = true 
      return 
     } 
     isExecuting = true 
     let request = NetworkingRouter.signIn(credentials: credentials).urlRequest 
     sessionTask = localURLSession.dataTask(with: request) 
     guard let task = sessionTask else { fatalError("Failed to get task") } 
     task.resume() 
    } 

    // MARK: - URL Session Delegate 

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { 
     if isCancelled { 
      isFinished = true 
      sessionTask?.cancel() 
      return 
     } 
     guard let statusCode = (response as? HTTPURLResponse)?.statusCode else { fatalError("Could not determine status code") } 
     setError(from: statusCode) 
     completionHandler(disposition(from: statusCode)) 
    } 

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 
     if isCancelled { 
      guard let task = sessionTask else { fatalError("Failed to get task") } 
      task.cancel() 
      return 
     } 
     receivedData.append(data) 
    } 

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 
     defer { isFinished = true } 
     if isCancelled { 
      guard let task = sessionTask else { fatalError("Failed to get task") } 
      task.cancel() 
     } 
     if let statusCode = (task.response as? HTTPURLResponse)?.statusCode { setError(from: statusCode) } else if let taskError = error as? NSError { setError(from: taskError) } 
     if self.error == nil { 
      guard let taskResponse = task.response else { fatalError("Invalid response") } 
      setAuthenticationCookie(from: taskResponse) 
      processData() 
     } 
    } 

    // MARK: - Helpers 

    /// Handles the processing of the data received from the data task. 
    private func processData() { 
     currentUser = UserModelCreator.user(from: receivedData) 
    } 

    /// Handles the persistence of the returned cookie from the request's response. 
    private func setAuthenticationCookie(from response: URLResponse) { 
     guard let storage = cookieStorage else { fatalError() } 
     let cookiePersistenceManager = ResponseCookiePersistenceManger(storage: storage) 
     cookiePersistenceManager.removePreviousCookies() 
     guard let httpURLResponse = response as? HTTPURLResponse else { fatalError("Invalid response type") } 
     if let cookie = ResponseCookieParser.cookie(from: httpURLResponse) {cookiePersistenceManager.persistCookie(cookie: cookie) } 
    } 

    /// Handles the return of a specified HTTP status code. 
    /// - parameter statusCode: The status code. 
    private func setError(from statusCode: Int) { 
     switch statusCode { 
     case 200: error = nil 
     case 401: error = .invalidCredentials 
     default: error = .generic 
     } 
    } 

    /// Returns a `URLResponse.ResponseDisposition` for the specified HTTP status code. 
    /// - parameter code: The status code. 
    /// - Returns: A disposition. 
    private func disposition(from code: Int) -> URLSession.ResponseDisposition { 
     switch code { 
     case 200: return .allow 
     default: return .cancel 
     } 
    } 

    /// Handles the return of an error from a network request. 
    /// - parameter error: The error. 
    private func setError(from error: NSError) { 
     switch error.code { 
     case Int(CFNetworkErrors.cfurlErrorTimedOut.rawValue): self.error = .requestTimedOut 
     case Int(CFNetworkErrors.cfurlErrorNotConnectedToInternet.rawValue): self.error = .noInternetConnection 
     default: self.error = .generic 
     } 
    } 

} 

を、すべてが動作するかどうかを確認するために、私が印刷されている期待されるすべてのデータで、その結果、viewDidAppear:で操作を呼び出す:

import UIKit 

class ViewController: UIViewController { 

    override func viewDidAppear(_ animated: Bool) { 
     super.viewDidAppear(animated) 

     let credentials = UserCredentials(emailAddress: "[email protected]", password: "xxxxxxxxxxxxxxxxxx") 
     let signInOp = SignInOperation(credentials: credentials) 

     let printOperation = Operation() 
     printOperation.addDependency(signInOp) 
     printOperation.completionBlock = { 
      if let error = signInOp.error { return print("\n====> Sign-in Error: \(error.message)\n") } 
      if let user = signInOp.currentUser { print("\n====> User: \(user)\n") } 
    } 

     let queue = OperationQueue() 
     queue.addOperations([signInOp, printOperation], waitUntilFinished: false) 
    } 

} 

しかし、インストゥルメンツでLeaksプロファイラを使用する場合、私はいくつかの驚くべきデータを取得します。ここで開始する場所

Screenshot of Leaks profiler run

私は本当に知りません。検出されたリークのいずれかをクリックすると、リークが原因であると私のコードには拘束されません。私はいくつかのチュートリアルを見てAppleのドキュメントを読んだが、漏れがどこから来ているのか把握しようとしている。ばかげているようです/

私は強い参照サイクルを持っている私のコードのどこにも見当たらないので、421の検出されたリークを解決する方法を理解するための助けを求めています。 sessionTask & localURLSession

答えて

2

それは私がは私SignInOperationサブクラスで2次のプロパティ二つ強い参照サイクルを、持っていないことが判明します。

これらのプロパティweakを行った後、私はもはや漏れが検出されていません:戻って来て、答えを提供するための

/// The URL session that is used for coordinating tasks used for sign-in. 
private weak var localURLSession: URLSession { return URLSession(configuration: localConfiguration, delegate: self, delegateQueue: nil) } 

/// The configuration used for configuring the URL session used for sign-in. 
private weak var localConfiguration: URLSessionConfiguration { return .ephemeral } 

Screenshot of Leaks profiler run

+0

感謝を。あなたは他の人がそれから学ぶことができるように修正しなければならないコードを投稿できますか? (あなたが戻ってきたら、すぐにあなたの答えを受け入れてください。) –

+0

@DuncanC絶対に!私はコードの変更を提供するために私の答えを編集しました。 –

関連する問題