Concurrence accessible Swift 6.2
Modèles pour adopter le modèle de concurrence de Swift 6.2, où le code s'exécute par défaut sur un seul fil d'exécution et la concurrence est introduite explicitement. Élimine les erreurs courantes de course aux données sans sacrifier les performances.
Quand activer
- Migration de projets Swift 5.x ou 6.0/6.1 vers Swift 6.2
- Résolution des erreurs de compilation liées à la sécurité des courses aux données
- Conception d'architecture d'application basée sur MainActor
- Déchargement de travaux CPU intensifs vers des threads d'arrière-plan
- Implémentation de conformités de protocole sur des types isolés par MainActor
- Activation des paramètres de construction « Approachable Concurrency » dans Xcode 26
Problème central : Déchargement implicite en arrière-plan
Dans Swift 6.1 et versions antérieures, les fonctions async pouvaient être implicitement déchargées vers des threads d'arrière-plan, provoquant des erreurs de course aux données même dans du code apparemment sûr :
// Swift 6.1 : ERREUR
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
// Erreur : Envoyer 'self.photoProcessor' risque de provoquer des courses aux données
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Swift 6.2 corrige cela : les fonctions async restent sur l'acteur appelant par défaut.
// Swift 6.2 : OK — async reste sur MainActor, pas de course aux données
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Modèle central — Conformités isolées
Les types MainActor peuvent désormais se conformer en toute sécurité à des protocoles non isolés :
protocol Exportable {
func export()
}
// Swift 6.1 : ERREUR — traverse du code isolé par MainActor
// Swift 6.2 : OK avec conformité isolée
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
}
Le compilateur garantit que la conformité n'est utilisée que sur l'acteur principal :
// OK — ImageExporter est aussi @MainActor
@MainActor
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Sûr : même isolation d'acteur
}
}
// ERREUR — le contexte non isolé ne peut pas utiliser la conformité MainActor
nonisolated struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Erreur : la conformité isolée par MainActor ne peut pas être utilisée ici
}
}
Modèle central — Variables globales et statiques
Protégez l'état global/statique avec MainActor :
// Swift 6.1 : ERREUR — le type non-Sendable peut avoir un état mutable partagé
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Erreur
}
// Correction : Annoter avec @MainActor
@MainActor
final class StickerLibrary {
static let shared: StickerLibrary = .init() // OK
}
Mode d'inférence par défaut MainActor
Swift 6.2 introduit un mode où MainActor est inféré par défaut — aucune annotation manuelle nécessaire :
// Avec l'inférence par défaut MainActor activée :
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Implicitement @MainActor
}
final class StickerModel {
let photoProcessor: PhotoProcessor
var selection: [PhotosPickerItem] // Implicitement @MainActor
}
extension StickerModel: Exportable { // Conformité implicitement @MainActor
func export() {
photoProcessor.exportAsPNG()
}
}
Ce mode est facultatif et recommandé pour les applications, scripts et autres cibles exécutables.
Modèle central — @concurrent pour le travail en arrière-plan
Lorsque vous avez besoin de parallélisme réel, déchargez explicitement avec @concurrent :
Important : Cet exemple nécessite les paramètres de construction Approachable Concurrency — SE-0466 (isolation par défaut MainActor) et SE-0461 (NonisolatedNonsendingByDefault). Avec ces paramètres activés,
extractStickerreste sur l'acteur de l'appelant, rendant l'accès à l'état mutable sûr. Sans ces paramètres, ce code a une course aux données — le compilateur le signalera.
nonisolated final class PhotoProcessor {
private var cachedStickers: [String: Sticker] = [:]
func extractSticker(data: Data, with id: String) async -> Sticker {
if let sticker = cachedStickers[id] {
return sticker
}
let sticker = await Self.extractSubject(from: data)
cachedStickers[id] = sticker
return sticker
}
// Déchargement du travail coûteux vers le pool de threads concurrentes
@concurrent
static func extractSubject(from data: Data) async -> Sticker { /* ... */ }
}
// Les appelants doivent utiliser await
let processor = PhotoProcessor()
processedPhotos[item.id] = await processor.extractSticker(data: data, with: item.id)
Pour utiliser @concurrent :
- Marquez le type conteneur comme
nonisolated - Ajoutez
@concurrentà la fonction - Ajoutez
asyncsi ce n'est pas déjà asynchrone - Ajoutez
awaitaux sites d'appel
Décisions de conception clés
| Décision | Justification |
|---|---|
| Monothreadé par défaut | Le code le plus naturel est exempt de courses aux données ; la concurrence est facultative |
| Async reste sur l'acteur appelant | Élimine le déchargement implicite qui provoquait des erreurs de courses aux données |
| Conformités isolées | Les types MainActor peuvent se conformer à des protocoles sans contournements dangereux |
@concurrent explicite | L'exécution en arrière-plan est un choix de performance délibéré, pas accidentel |
| Inférence par défaut MainActor | Réduit les annotations @MainActor superflues pour les cibles d'application |
| Adoption facultative | Chemin de migration non destructif — activez les fonctionnalités progressivement |
Étapes de migration
- Activer dans Xcode : Section Swift Compiler > Concurrency dans les paramètres de construction
- Activer dans SPM : Utiliser l'API
SwiftSettingsdans le manifeste du paquet - Utiliser les outils de migration : Modifications automatiques du code via swift.org/migration
- Commencer par les valeurs par défaut MainActor : Activer le mode d'inférence pour les cibles d'application
- Ajouter
@concurrentlà où nécessaire : Profiler d'abord, puis décharger les chemins chauds - Tester en profondeur : Les problèmes de courses aux données deviennent des erreurs de compilation
Bonnes pratiques
- Commencez sur MainActor — écrivez du code monothreadé d'abord, optimisez ensuite
- Utilisez
@concurrentuniquement pour les travaux CPU intensifs — traitement d'image, compression, calculs complexes - Activez le mode d'inférence MainActor pour les cibles d'application majoritairement monothreadées
- Profiler avant de décharger — utilisez Instruments pour trouver les véritables goulots d'étranglement
- Protégez les variables globales avec MainActor — l'état mutable global/statique nécessite une isolation par acteur
- Utilisez les conformités isolées plutôt que des contournements
nonisolatedou des wrappers@Sendable - Migrez progressivement — activez les fonctionnalités une par une dans les paramètres de construction
Anti-patrons à éviter
- Appliquer
@concurrentà chaque fonction async (la plupart n'ont pas besoin d'exécution en arrière-plan) - Utiliser
nonisolatedpour supprimer les erreurs du compilateur sans comprendre l'isolation - Conserver les motifs
DispatchQueuehérités alors que les acteurs offrent la même sécurité - Sauter les vérifications
model.availabilitydans le code des modèles Foundation liés à la concurrence - Combattre le compilateur — s'il signale une course aux données, le code a un vrai problème de concurrence
- Supposer que tout code async s'exécute en arrière-plan (Swift 6.2 par défaut : reste sur l'acteur appelant)
Quand utiliser
- Tous les nouveaux projets Swift 6.2+ (Approachable Concurrency est la valeur par défaut recommandée)
- Migration d'applications existantes de Swift 5.x ou 6.0/6.1 vers la concurrence
- Résolution des erreurs de compilation liées à la sécurité des courses aux données lors de l'adoption de Xcode 26
- Construction d'architectures d'application centrées sur MainActor (la plupart des applications UI)
- Optimisation des performances — déchargement de calculs lourds spécifiques en arrière-plan