AppleのInstrumentsツールを使用して、アプリケーションの現在の進行状況をチェックし、早急にリークを管理しています。私は多くのリークのを持っているようですが、どこから来ているのか分かりません。私の421のメモリリークはどこから来ますか?
私のアプリケーションでは、Operation
のサブクラスであるSignInOperation
があります。また、それはURLSessionDataDelegate
に準拠しているので、補完ハンドラを使用せずに自分の要求を処理できます。たとえば、インスタンスにインスタンスを追加する場合、UIの更新を実行する操作では、SignInOperation
インスタンスが依存関係になるため、SignInOperation
のerror
および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プロファイラを使用する場合、私はいくつかの驚くべきデータを取得します。ここで開始する場所
私は本当に知りません。検出されたリークのいずれかをクリックすると、リークが原因であると私のコードには拘束されません。私はいくつかのチュートリアルを見てAppleのドキュメントを読んだが、漏れがどこから来ているのか把握しようとしている。ばかげているようです/
私は強い参照サイクルを持っている私のコードのどこにも見当たらないので、421の検出されたリークを解決する方法を理解するための助けを求めています。 sessionTask
& localURLSession
:
感謝を。あなたは他の人がそれから学ぶことができるように修正しなければならないコードを投稿できますか? (あなたが戻ってきたら、すぐにあなたの答えを受け入れてください。) –
@DuncanC絶対に!私はコードの変更を提供するために私の答えを編集しました。 –