Sendable — είναι ένα πρωτόκολλο Swift που σημαιώνει τυπους ασφαλείς για μεταφορά μεταξύ νημάτων. Κατά την εργασία με συναρτήσεια (actor, async/await, Task), ο μεταγλωτιστής Swift απαιτεί όλα τα δεδομένα που μεταφέρονται μεταξύ απομονωμένων περιεχομένων να συμμορφώνονται με το Sendable. Αυτό αποκλείει την τυχαία μεταφορά μη ασφαλών τυπων που μπορούν να οδηγήσουν σε συνθήκες ανταγωνισμού δεδομένων. Το πρωτόκολλο λειτουργεί ως σύμβολο: ένας τύπος που συμμορφώνεται με το Sendable εγγυάται την απουσία εσωτερικής μη συγχρονισμένης κατάστασης. Σύμφωνα με το WWDC 2021, το πρωτόκολλο Sendable είναι υποχρεωτικό στοιχείο κατά τον σχεδιασμό ασφαλούς πολυνεματικής αρχιτεκτονικής.
Κύρια
Sendable — είναι ένα πρωτόκολλο από την τυπική βιβλιοθήκη Swift (SE-0302) που σημαιώνει τυπους ασφαλείς για μεταφορά μεταξύ τομέων απομόνωσης. Ως τομέας απομόνωσης νοείται ένας actor, μια εργασία Task ή ένα πλαίσιο @MainActor. Το Sendable δεν περιέχει απαιτήσεις — λειτουργεί ως δείκτης ασφαλείας.
Πριν το Swift 5.5, οι αναπτυκτές μπορούσαν να μεταφέρουν οποιοδήποτε αντικείμενο μεταξύ νημάτων μέσω DispatchQueue, και ο μεταγλωτιστής δεν έλεγχε αν αυτό ήταν ασφαλές. Sendable κλείνει αυτό το κενό: τώρα ο μεταγλωτιστής ο ίδιος παρακολουθεί τις μεταφορές μεταξύ νημάτων και μπλοκάρει τις μη ασφαλείς. Αυτό καθιστά τον συναρτησιακό κώδικα πιο αξιόπιστο ακόμη και πριν την εκτέλεση.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
Στο παράδειγμα, το UserProfile μπορεί να μεταφερθεί με ασφάλεια μεταξύ νημάτων, καθώς είναι μια δομή με σταθερές ιδιότητες τυπων Sendable. Το NonSendableClass θα προκαλέσει σφάλμα μεταγλώτισης κατά την προσπάθεια μεταφοράς μέσω await ή σε Task.
Οι τύποι Sendable στο Swift χωρίζονται σε τρεις κατηγορίες: τύποι τιμής με ιδιότητες Sendable, τελικές κλάσεις με αμετάβλητη κατάσταση και συναρτήσεις/κλείσιματα που σημαιώνονται με @Sendable. Ο μεταγλωτιστής συνάγει αυτόματα τη συμμόρφωση με το Sendable για τους τύπους τιμής.
Struct, enum και tuple γίνονται αυτόματα Sendable εάν όλες οι ιδιότητές και οι σχετιζόμενες τιμές τους είναι επίσης Sendable. Αυτή είναι μια συντηρητική προσέγγιση: εάν ακόμη και μία ιδιότητα δεν συμμορφώνεται με το Sendable, ο λόκληρος τύπος δεν θα θεωρηθεί ασφαλής.
| Τύπος | Sendable | Προϋπόθεση |
|---|---|---|
| Struct | Ναι (χωρίς ρητή ένδειξη) | Όλες οι ιδιότητες Sendable |
| Enum | Ναι (χωρίς ρητή ένδειξη) | Όλες οι σχετιζόμενες τιμές Sendable |
| Final class | Ναι (με ρητή ένδειξη) | Όλες οι ιδιότητες let Sendable, χωρίς var |
| Non-final class | Όχι | Δεν μπορεί να είναι Sendable λόγω κληρονομίας |
Όλοι οι ενσωματωμένοι τύποι Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — συμμορφώνονται με το Sendable. Αυτό καθιστά τη σύνθεση τύπων ασφαλή από προκαταβολή. Ο αναπτυκτής πρέπει να προσέχει μόνο στις προσαρμοσμένες κλάσεις.
@unchecked Sendable — είναι ένας μηχανισμός που επιτρέπει σε μια κλάση να δηλώνει ρητά τον εαυτό της ως Sendable, παρακάμπτοντας τους ελέγχους του μεταγλωτιστή. Ο αναπτυκτής αναλαμβάνει την ευθύνη για την ασφάλεια νήματος τετοίας κλάσης. Είναι χρήσιμο για γέφυρες Objective-C και βελτιστοποιημένες δομές.
Το @unchecked Sendable εφαρμόζεται όταν μια κλάση εσωτερικά εγγυάται την ασφάλεια μέσω κλειδωμάτων ή ατομικών λειτουργιών, αλλά ο μεταγλωτιστής δεν μπορεί να το ελέγξει στατικά. Για παράδειγμα, μια κλάση με os_unfair_lock ή pthread_mutex_t — η ασφάλεια νήματος παρέχεται από τον κώδικα, αλλά η Swift δεν το βλέπει.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
Χρησιμοποιείτε @unchecked Sendable με προσοχή. Αυτό είναι ένα σαφές σήμα για άλλους αναπτυκτές: “Το έλεγξα, είναι ασφαλές”. Ένα σφάλμα στην υλοποίηση μπορεί να οδηγήσει σε δυσκόλως ανιχνεύσιμες συνθήκες ανταγωνισμού. Πριν από την εφαρμογή @unchecked, βεβαιωθείτε ότι ο τύπος πραγματικά δεν μπορεί να επαναγραφεί ως τύπος τιμής.
Actor και Sendable είναι δύο πλευρές του ίδιου νομίσματος. Ο Actor απομονώνει την κατάστασή του, αλλά για την ανταλλαγή δεδομένων με τον εξωτερικό κόσμο πρέπει να επιστρέφει τύπους Sendable. Εάν μια μέθοδος actor επιστρέφει έναν μη Sendable τύπο, ο μεταγλωτιστής εκπέμπει πρειδοποίηση ή σφάλμα.
Όταν ένας actor στέλνει δεδομένα σε εξωτερικό κώδικα, αυτά τα δεδομένα διασχίζουν το όριο απομόνωσης. Sendable εγγυάται ότι ο παραλήπτης μπορεί να τα χρησιμοποιήσει με ασφάλεια εκτός του actor. Ο ίδιος ο actor παραμένει απομονωμένος — η εσωτερική κατάστασή του δεν αποκαλύπτεται.
struct AccountSnapshot: Sendable {
let id: UUID
let balance: Double
let lastUpdated: Date
}
actor BankActor {
private var balance: Double = 0
func snapshot() async -> AccountSnapshot {
return AccountSnapshot(
id: UUID(),
balance: balance,
lastUpdated: Date()
)
}
}
AccountSnapshot — είναι μια δομή Sendable που περιέχει μόνο ιδιότητες let τυπων Sendable. Αυτή η προσέγγιση είναι best practice για την εξαγωγή δεδομένων από έναν actor. Το στιγμιότυπο της κατάστασης μεταφέρεται κατά αξία, και ο actor δεν χάνει τον έλεγχο της κατάστασής του.
@Sendable — είναι ένα χαρακτηριστικό για συναρτήσεις και κλείσιματα που εγγυάται ότι το κλείσιμο δεν συλλαμβάνει μη Sendable δεδομένα με μεταβλητό τρόπο. Όταν ένα κλείσιμο μεταφέρεται σε Task ή μέθοδο actor, πρέπει να είναι Sendable.
Ο μεταγλωτιστής ελέγχει ότι το @Sendable κλείσιμο δεν συλλαμβάνει μεταβλητές αναφορές σε κλάσεις. Η σύλληψη ιδιοτήτων let τυπων Sendable επιτρέπεται. Η σύλληψη μιας μεταβλητής var ενός τύπου αναφοράς θα προκαλέσει σφάλμα, καθώς το κλείσιμο μπορεί να εκτελεστεί ταυτόχρονα με τη μεταβολή.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — η σύλληψη var μεταβάλλει τον απαριθμητή, σφάλμα μεταγλώτισης
performAsync { // ❌ Μεταβολή του συλληφθέντος var
print(constant)
}
Ο κανόνας είναι απλός: ένα @Sendable κλείσιμο μπορεί να συλλάβει μόνο εκείνα τα δεδομένα που είναι αυτά τα ίδια Sendable και δεν θα μεταβληθούν από τον εξωτερικό περιβάλλοντα. Για κλάσεις, επιτρέπεται η σύλληψη της ασθενούς αναφοράς weak self εάν η κλάση δεν είναι σημασμένη ως Sendable. Αυτό αποτρέπει τους κλασικούς κύκλους συγκράτησης και τις συνθήκες ανταγωνισμού.
Συχνές Ερωτήσεις
Sendable — είναι ένας δείκτης που λέει στον μεταγλωτιστή: “Αυτός ο τύπος μπορεί να μεταφορθεί με ασφάλεια μεταξύ νημάτων”. Οι δομές και οι σταθερές είναι συνήθως Sendable από προκαταβολή, οι κλάσεις — όχι, εκτός εάν δηλωθεί ρητά.
Οι τύποι τιμής (struct, enum) με ιδιότητες Sendable, οι τελικές κλάσεις με αμετάβλητη κατάσταση, καθώς και όλοι οι βασικοί τύποι Swift: Int, String, Double, Bool, Array, Dictionary, Optional — συμμορφώνονται αυτόματα με το Sendable.
@unchecked Sendable — είναι μια μέθοδος για να κηρυχθεί μια κλάση ως Sendable χωρίς στατικό έλεγχο από τον μεταγλωτιστή. Ο αναπτυκτής εγγυάται την ασφάλεια αυτόνομα, συχνά μέσω κλειδωμάτων ή ατομικών λειτουργιών.
Ο Actor απομονώνει την κατάστασή του, αλλά κατά την επιστροφή δεδομένων σε εξωτερικό κώδικα, αυτά τα δεδομένα διασχίζουν το όριο απομόνωσης. Sendable εγγυάται ότι ο παραλήπτης δεν θα συναντήσει συνθήκες ανταγωνισμού κατά τη χρήση αυτών των δεδομένων εκτός του actor.
Προσθέστε το πρωτόκολλο Sendable σε μια τελική κλάση της οποίας όλες οι ιδιότητες είναι σταθερές τυπων Sendable. Εάν η κλάση χρησιμοποιεί κλειδώματα, μπορεί να εφαρμοστεί @unchecked Sendable, αλλά αυτό απαιτεί προσοχή.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης