我正试图解决内存泄漏问题。我有以下用于获取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
行中,出现以下描述的内存泄漏:
任何想法可能是什么原因,或者如何找出?我对Xcode和Swift相当陌生。