В Swift я пытаюсь понять ход логики при использовании Sendable, неизолированных и актеров.IOS

Программируем под IOS
Ответить
Anonymous
 В Swift я пытаюсь понять ход логики при использовании Sendable, неизолированных и актеров.

Сообщение Anonymous »

Насколько я понимаю, TripsFeedViewModel работает на MainActor из-за наблюдаемости, поэтому этот класс в основном изолирован от актера, MainActor. Так что в некотором смысле это предотвращает гонку данных. Затем, когда мы используем tripService.getTrips(), основной поток передаст его фоновому потоку для ожидания в очереди/блокировке актера TripService. Как только поток разрешен в актере TripService, он инициализирует NetworkRequestService, который затем привязывается к этому актеру, как и его функции. Поэтому, когда мы вызываем sendRequest, который является методом NetworkRequestService, он будет выполняться на актере TripService, и поток будет ждать, пока не будет получен ответ, удерживая блокировку. Однако если мы помечаем его как неизолированный, его можно передать фоновому потоку для завершения, после чего поток сможет его подхватить и снова войти в очередь. поэтому мы должны пометить его как неизолированный, а NetworkRequestService как отправляемый, потому что его безопасно отправлять между потоками, поскольку нет изменяемых состояний. Затем, когда дело доходит до PrivateTripResponse, технически его всегда можно отправить, потому что это структура и копия создается вместо ссылок на нее. Поскольку PrivateTripResponse соответствует Codable, он работает на MainActor, поэтому пометка его как неизолированного позволяет ему декодироваться и на фоновом актере, и это безопасно, поскольку его можно отправлять. Таким образом, sendbale и неизолированный работают рука об руку, давая компилятору знать, что что-то потокобезопасно для отправки между потоками с гонками данных. Будет ли это объяснение отражать то, что происходит во всем этом процессе?
import Foundation

@Observable
class TripsFeedViewModel {
var trips: [Trip] = []

private let tripService: TripServiceProtocol

init(tripService: TripServiceProtocol) {
self.tripService = tripService
}

func getTrip() async -> Void {
do {
let trips = try await tripService.getTrips()
await MainActor.run {
self.trips = trips.compactMap {
Trip(
id: $0.id,
tripName: $0.tripName,
location: $0.location,
budget: $0.budget,
isFavorite: $0.isFavorite,
startDate: $0.startDate,
endDate: $0.endDate,
imageURLString: $0.imageURL
)
}
}
} catch {
print("There was an error get your trips: \(error.localizedDescription)")
}
}
}

import Foundation

actor TripService: TripServiceProtocol {
private let networkService: NetworkRequestService
private let keychainService: KeychainService
private var activeTask: Task?

init(networkService: NetworkRequestService, keychainService: KeychainService) {
self.networkService = networkService
self.keychainService = keychainService
}

func getTrips() async throws -> [TripPrivateResponse] {
if let existing = activeTask {
return try await existing.value
}

let task = Task {
guard let url = URL(string: "local host url") else {
throw APIError.invalidURL
}

var request = URLRequest(url: url)
request.httpMethod = "GET"

if let token = keychainService.getToken() {
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}

return try await networkService.sendRequest(request: request, responseType: [TripPrivateResponse].self)
}

activeTask = task

defer { activeTask = nil }

return try await task.value
}
}

struct TripPrivateResponse: nonisolated Codable, Sendable {
let id: Int
let tripName: String
let location: String
let budget: Int
let isFavorite: Bool
let startDateString: String
let endDateString: String
let imageURLString: String

enum CodingKeys: String, CodingKey {
case id
case tripName = "title"
case location
case budget
case isFavorite = "is_favorite"
case startDateString = "start_date"
case endDateString = "end_date"
case imageURLString = "cover_image_url"
}
}

final class NetworkRequestService: Sendable {
// MARK: Sends the request and returns the response from FastAPI
nonisolated func sendRequest(request: URLRequest, responseType: Output.Type) async throws -> Output {
do {
/// 1. this sends the data to FastAPI then waits for a response
let (data, response) = try await URLSession.shared.data(for: request)

print("FASTAPI RESPONSE: \(String(data: data, encoding: .utf8) ?? "No Data")")

/// 2. checks the response (convert it to HTTPURLResponse type) making sure it has a successful status code
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
throw APIError.invalidResponse
}

/// 3. decode it to the DTO (UserPrivateResponse)
return try JSONDecoder().decode(responseType, from: data)
} catch let error as URLError {
throw APIError.networkError(error)
} catch let error as DecodingError {
throw APIError.decoding(error)
}
}
}
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»