Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
191 views
in Technique[技术] by (71.8m points)

swift - with or without [weak self]

I have a very strange situation. My server is currently down and getting 503 http status code. Based on the given code as follows, code gets into the if condition and but when I put debug point to the let error = self?.decodeErrorMessage(data: data, statusCode: response.statusCode). then it is jumping from if condition and executes the else if condition.

ClientViewModel.swift

let networkRequest = CustomNetworkRequest(headers: headers, httpMethod: .get, httpBody: nil, parameters: nil, url: url)

let customNetworker = CustomNetworker(urlSession: URLSession(configuration: config))
  customNetworker.dataRequest(networkRequest, successHandler: {[weak self] data in
    self?.parseData(data)
    completion(nil)
  }, failureHandler: { [weak self] error in
    completion(error)
})

CustomNetworker.swift

final class CustomNetworker {
  private let urlSession: URLSession

  public init(urlSession: URLSession) {
    self.urlSession = urlSession
  }
  func dataRequest(input parameters here) {
    let task = urlSession.dataTask(with: urlRequest) {[weak self] data, response, error in
     if let response = response as? HTTPURLResponse , 300...599 ~= response.statusCode,
     let data = data,
     let error = self?.decodeErrorMessage(data: data, statusCode: response.statusCode) {
    failureHandler(error)
    } else if let error = error {
     failureHandler(error)
    } else if let data = data {
      successHandler(data)
    } else {
      failureHandler(error)
    }
  }
  task.resume()
  }
}

However, If I remove [weak self] then it works as expected, it calls that method. How should I resolve the issue properly?

let task = urlSession.dataTask(with: urlRequest) {data, response, error in
  if let response = response as? HTTPURLResponse , 300...599 ~= response.statusCode,
    let data = data,
    let error = self.decodeErrorMessage(data: data, statusCode: response.statusCode) {
    failureHandler(error)
  }
  else if let error = error {
    failureHandler(error)
  } else if let data = data {
    successHandler(data)
  } else {
    failureHandler(error)
    )
  }
}
task.resume()

decodeErrorMessage method

private func decodeErrorMessage(data: Data, statusCode: Int) -> CustomError?  {
   // not coming into this method
    if let errorData = try? JSONDecoder.convertFromSnakeCaseDecoder.decode(CustomServiceError.self, from: data) as CustomServiceError? {
      if let errorData = errorData {
        return formCustomError(something here)
      }
      return CustomError.genericError(debugMessage: debugMessage, sourceError: nil)
    } else if let errorMessage = StatusCode(rawValue: statusCode) {
      let debugMessage = String(data:data, encoding: .utf8)
      return formCustomError(something here)
    } else {
      let debugMessage = String(data:data, encoding: .utf8)
      return formCustomError(something here)
    }
  }
question from:https://stackoverflow.com/questions/66051875/with-or-without-weak-self

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The issue is that you’ve made customNetworker, the CustomNetworker, a local variable and you lose your strong reference to it as soon as this local variable falls out of scope. When the dataRequest(input:) method uses a [weak self] reference, it’s saying that you don’t want dataRequest(input:) to keep a strong reference to this CustomNetworker instance, either. As a result, with no strong references, the CustomNetworker will be deallocated.

You have a few alternatives:

  1. If you remove the [weak self] reference in the closure inside dataRequest(input:), then it will retain the CustomNetworker until the closure runs. As long as you don’t save this failureHandler (and other closures) as stored properties, you should be fine. But don’t just throw in a [weak self] because you read somewhere that you should always use weak references. Use weak references where you need to avoid strong reference cycles, which would not appear to be the case here.

  2. The other alternative is to refactor this code, moving this network request routine, dataRequest(input:) to some more long-lived object.

    E.g., we often have a long-lived network manager object that maintains the URLSession and has all these network related methods. Then, assuming you’re keeping a strong reference to this network manager instance elsewhere, you don’t need to worry about the object being deallocated when it falls out of scope. This also saves you from having to pass in the URLSession object for every request. A caller should not be dealing with these URLSession instances, anyway.

    For example, I often use a singleton network layer (kind of equivalent to URLSession.shared pattern):

    final class NetworkManager {
        static let shared = NetworkManager()
    
        private var session: URLSession = .shared // or create and configure this session as you need; but one session object is all you need
    
        private init() { }
    
        @discardableResult
        func performRequest(...) -> URLSessionTask {
            ...
            let task = session.dataTask(...) { [weak self] data, response, error in
                ...
            }
            task.resume()
            return task
        }
    }
    

    Then you can do things like:

    NetworkManager.shared.performRequest(...) { [weak self] ... in
        ...
    }
    
  3. The other alternative is to just keep a strong reference to this customNetworker variable (e.g. make it a property rather than a local variable). That will prevent the CustomNetworker object from falling out of scope and becoming nil because there are no more strong references to it.


Somewhat unrelated, but it would appear that you are creating a URLSession object when you instantiate your CustomNetworker object, and subsequently never invalidate it. This will leak. You should create a single URLSession object and use it for all subsequent network requests.

If you do create multiple URLSession objects, you must invalidate each one when you’re done with it (e.g., finishTasksAndInvalidate), or else you will leak. But if you just have one session that you keep alive for all future network requests (which is preferred), then that is not necessary.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...