CFNetwork的Swift Combine内存泄漏

时间:2020-06-19 23:50:50

标签: swift xcode swiftui combine

我正试图解决内存泄漏问题。我有以下用于获取API请求的类:

public struct Service {

  public let baseURL: URL
  public let session: URLSession

  public init (baseURL: URL, session: URLSession) {
    self.baseURL = baseURL
    self.session = session
  }

  public struct Response {
    public let data: Data
    public let response: URLResponse
  }



  public enum ServiceError: Error {
    case api(title: String, messages: [String])
    case other(Error)
  }



  struct ServiceErrorResponse: Decodable {
    let response: ErrorResponse

    enum CodingKeys: String, CodingKey {
      case response = "error"
    }
  }



  struct ErrorResponse: Decodable {
    let title: String
    let messages: [String]
  }



  public enum HTTPMethod: String {
    case get = "GET"
    case put = "PUT"
    case post = "POST"
    case patch = "PATCH"
    case delete = "DELETE"
  }



  public func run(_ request: URLRequest) -> AnyPublisher<Response, ServiceError> {
    return session
      .dataTaskPublisher(for: request)
      .tryMap { data, response in
        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
          let error = try JSONDecoder().decode(ServiceErrorResponse.self, from: data)
          let title = error.response.title
          let messages = error.response.messages

          print(error.response)

          throw ServiceError.api(title: title, messages: messages)
        }

        return Response(data: data, response: response)
      }
      .mapError { err in
        let error = err is ServiceError ? err : ServiceError.other(err)
        return error as! Service.ServiceError
      }
      .eraseToAnyPublisher()
  }



  public func fetch(
    _ path: String,
    method: HTTPMethod = .get,
    params: Data? = nil
  ) -> AnyPublisher<Response, ServiceError> {

    let url: URL

    if let params = params, method == .get {
      url = buildGetURLWithParams(path: path, params: params)!
    }
    else {
      url = baseURL.appendingPathComponent(path)
    }


    var request = URLRequest(url: url)
    request.httpMethod = method.rawValue
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")


    if let params = params, method != .get {
      request.httpBody = params
    }

    return run(request)
  }


  private func buildGetURLWithParams(path: String, params: Data) -> URL? {
    if let json = try? JSONSerialization.jsonObject(with: params, options: []) as? [String: String] {
      var urlComponents = URLComponents(
        url: baseURL.appendingPathComponent(path),
        resolvingAgainstBaseURL: false
      )
      urlComponents?.queryItems = json.map { URLQueryItem(name: $0, value: $1) }

      return urlComponents?.url
    }
    else { return nil }
  }
}

然后我使用以下命令从应用程序发出请求:

typealias ServiceResponse = Service.Response
typealias ServiceError = Service.ServiceError
typealias ServiceMethod = Service.HTTPMethod

    enum MyAPI {

      static let service = Service(
        baseURL: URL(string: "http://127.0.0.1:3000/api")!,
        session: URLSession(configuration: URLSessionConfiguration.default)
  )

  static func login(email: String, password: String) -> AnyPublisher<ServiceResponse, ServiceError> {
    let params = ["email": email, "password": password]
    let json = try! JSONEncoder().encode(params)

    return service.fetch("/login", method: .post, params: json)
  }
}

login函数获取响应并返回AnyPublisher,其用法如下:

enum UserAction {
  case login
  case loginSuccess(UserResponse)
  case loginFailure
  case logout



  static func login(email: String, password: String) -> Dispatch<AppAction> {
    return { dispatch in
      dispatch(.userAction(action: .login))


      return MyAPI.login(email: email, password: password)
        .map(\.data)
        .decode(type: UserResponse.self, decoder: JSONDecoder())
        .receive(on: DispatchQueue.main)
        .sink(
          receiveCompletion: { completion in
            if case .failure(let err) = completion {
              print("--------------------------")
              print("Retrieving data failed with error \(err)")
            }
          },
          receiveValue: { result in
            dispatch(.userAction(action: .loginSuccess(result))) // Here I have a memory leak

          }
      )
    }
  }
}

我正在模仿Redux之类的东西,其中调度操作会更改状态,因此我从UserAction登录返回“效果”,以获取调度功能。一切正常,但是在receiveValue行中,出现以下描述的内存泄漏:

enter image description here

任何想法可能是什么原因,或者如何找出?我对Xcode和Swift相当陌生。

0 个答案:

没有答案