Actor — είναι ένας τύπος που εισήχθη στο Swift 5.5+ και επιλύει το πρόβλημα των συνθηκών ανταγωνισμού δεδομένων σε επίπεδο γλώσσας. Σε αντίθεση με τα χειροκίνητα κλειδώματα και τις ουρές DispatchQueue, ο actor απομονώνει αυτόματα την κατάστασή του και συγχρονίζει την πρόσβαση σε αυτήν. Αυτό σημαίνει ότι δύο νήματα δεν μπορούν να τροποποιήσουν την ίδια ιδιότητα ενός τύπου actor ταυτόχρονα, καταργώντας τη συνθήκη ανταγωνισμού χωρίς επιπλέον προσπάθεια από τον πραγματοποιητή. Ο μηχανισμός βασίζεται στην εννοία της απομόνωσης actor, όπου ο μεταγλωττιστής παρακολουθεί την πρόσβαση στις ιδιότητες και τις μεθόδους του actor. Σύμφωνα με Apple, 2025, το μοντέλο actor είναι θεμελιώδες μέρος του συστήματος συναρμοτικότητας του Swift.
Κύρια σημεία
Actor — είναι ένας τύπος αναφοράς, παρόμοιος με την κλάση, αλλά με αυτόματη προστασία από συνθήκες ανταγωνισμού. Εισήχθη στο Swift 5.5 ως μέρος του συστήματος συναρμοτικότητας μαζί με async/await και Task. Ο actor εγγυάται ότι η μεταβλητή κατάστασή του δεν θα διαβαστεί ή γραφτεί ποτέ ταυτόχρονα από διαφορετικά νήματα χωρίς ρητή συγχρόνιση.
Για τη δήλωση actor χρησιμοποιείται η λέξη κλειδί actor, ακολουθούμενη από άγκυρα στοιχεία με τα μέλη του. Συντακτικά, ο actor μοιάζει με κλάση, αλλά η συμπεριφορά είναι ριζικά διαφορετική.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
Ο μεταγλωττιστής απομονώνει αυτόματα όλες τις ιδιότητες και μεθόδους του actor έτσι ώστε να είναι προσβάσιμες μόνο εντός του περιεχομένου actor. Η προσπάθεια πρόσβασης στο balance από εξωτερική του actor θα προκαλέσει σφάλμα μεταγλωττιστή εάν η κλήση δεν είναι σημασμένη ως async.
Απομόνωση δεδομένων — είναι η κλειδία έννοια του actor. Ο actor εγγυάται αμοιβαία αποκλειστική πρόσβαση στην κατάστασή του μέσω του μηχανισμού εκτελεστή actor (actor executor). Κάθε actor έχει τον δικό του εκτελεστή που επεξεργάζεται όλες τις προσβάσεις στα απομονωμένα μέλη του σειριακά.
Όταν κώδικας εκτός του actor καλεί τη μέθοδό του, η κλήση τοποθετείται στην ουρά του εκτελεστή actor. Ο actor εκτελεί μόνο μία εργασία τη φορά, εγγυώμενος την απουσία συνθηκών ανταγωνισμού. Αν δύο νήματα καλέσουν ταυτόχρονα τη deposit, η δεύτερη κλήση περιμένει την ολοκλήρωση της πρώτης.
let account = BankAccount(initialBalance: 1000.0)
// Ασύγχρονη κλήση — απαιτείται από εξωτερική του actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
Κάθε κλήση μεθόδου actor απαιτεί await, επειδή ο actor μπορεί να είναι απασχολημένος με άλλη εργασία. Αυτό δεν είναι σφάλμα, αλλά συνεδητή σχεδία για την αποφυγή συνθηκών ανταγωνισμού. Το Swift κάνει σιωπηρά τις getter και setter των ιδιοτήτων του actor ασύγχρονες, επομένως η ανάγνωση μιας ιδιότητας απαιτεί επίσης await.
Actor και κλάση είναι τύποι αναφοράς, αλλά η συμπεριφορά τους σε πολυνηματικό περιβάλλον διαφέρει ριζικά. Η κλάση δεν παρέχει καμία αυτόματη προστασία από συνθήκες ανταγωνισμού, ενώ ο actor την ενσωματώνει σε επίπεδο μεταγλωττιστή μέσω του συστήματος τύπων.
| Χαρακτηριστική | Actor | Class |
|---|---|---|
| Προστασία από ανταγωνισμό | Αυτόματη, σε επίπεδο μεταγλωττιστή | Απαιτεί χειροκίνητη συγχρόνιση |
| Πρόσβαση σε ιδιότητες | Μόνο μέσω await από εξωτερική | Άμεση, χωρίς συγχρόνιση |
| Κληρονόμηση | Μόνο από άλλους actor | Στανδαρδική κλασική |
| Συμμόρφωση με πρωτόκολλο | Μπορεί να συμμορφωθεί με πρωτόκολλα | Στανδαρδική |
| Απόδοση | Χαμηλό επιβάρυνση κατά την απομόνωση | Χωρίς συγχρόνιση — πιο γρήγορα |
Ο actor μπορεί να κληρονομήσει μόνο από άλλον actor και δεν μπορεί να κληρονομήσει από κλάση. Αυτό έγινε συνεδητά, επειδή η κλάση δεν έχει μηχανισμό απομόνωσης actor, και η ανάμειξη δύο μοντέλων θα οδηγούσε σε ακαθόριστη συμπεριφορά.
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
Ασύγχρονες κλήσεις — είναι ο μηχανισμός αλληλεπίδρασης με τον actor από εξωτερικό κώδικα. Επειδή ο actor απομονώνει την κατάστασή του, κάθε πρόσβαση στα μέλη του από εξωτερική απαιτεί await. Αυτό επιτρέπει στο Swift να εγγυάται ότι ο κώδικας που καλεί δεν αποκλείει το νήμα και ο actor μπορεί να επεξεργάζεται άλλα αιτήματα.
Εκτός από τους δηλωμένους τύπους actor, το Swift υποστηρίζει παγκόσμιους actor — το χαρακτηριστικό @MainActor, που σημαιώνει κλάσεις, ιδιότητες ή μεθόδους ως εκτελούμενες στο κύριο νήμα. Αυτό είναι ιδιαίτερα χρήσιμο κατά την εργασία με UIKit και SwiftUI.
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// Αυτός ο κώδικας είναι εγγυημένο ότι θα εκτελεστεί στο κύριο νήμα
title = "New Title"
}
}
Η χρήση του @MainActor εξαλείφει την ανάγκη χειροκίνητης κλήσης DispatchQueue.main.async, καθιστώντας τον κώδικα πιο καθαρό και ασφαλή. Ο μεταγλωττιστής ελέγχει ότι η εναλλαγή στο κύριο νήμα γίνεται σωστά.
Nonisolated — είναι μια λέξη κλειδί που επιτρέπει να σημαιώσετε μια μέθοδο ή υπολογιζόμενη ιδιότητα του actor ως μη απομονωμένη. Τέτοια μέλη δεν έχουν πρόσβαση στην απομονωμένη κατάσταση του actor, αλλά μπορούν να κληθούν χωρίς await από εξωτερική του actor.
Οι μεθοδοι nonisolated είναι χρήσιμες για υπολογισμούς που δεν εξαρτώνται από την μεταβλητή κατάσταση του actor. Για παράδειγμα, η μέθοδος formatBalance δεν έχει άμεση πρόσβαση στο balance, αλλά μόνο μορφοποιεί την τιμή που της μεταβιβάζεται — μια τέτοια μέθοδος μπορεί να γίνει με ασφάλεια nonisolated.
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
Τα nonisolated μέλη εκτελούνται συγχρόνα και δεν απαιτούν await. Ωστόσο, δεν μπορούν να διαβάσουν άμεσα τις απομονωμένες ιδιότητες του actor. Αν μια nonisolated μέθοδος χρειάζεται μια τιμή από τον actor, πρέπει να μεταβιβαστεί ως παράμετρος.
Reentrancy — είναι ένας μηχανισμός που επιτρέπει την εκ νέου είσοδο στον actor κατά τη διάρκεια αναμονής μιας ασύγχρονης κλήσης. Χωρίς reentrancy, ο actor θα μπορούσε να εμπλακεί για πάντα εάν μια μέθοδός του περιμένει μια άλλη, η οποία με τη σειρά περιμένει την πρώτη.
Όταν ο κώδικας εντός του actor εκτελεί await, ο actor αναστέλλει την τρέχουσα εργασία και μπορεί να επεξεργαστεί μια άλλη που έχει τεθεί στην ουρά. Μετά την ολοκλήρωση του await, η εργασία συνεχίζεται. Αυτό αποτρέπει deadlock, αλλά απαιτεί προσοχή: η κατάσταση του actor μεταξύ σημείων await μπορεί να αλλάξει.
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — σημείο εκ νέου εισόδου
let result = await fetchData(id: id)
// Η λευκοπαροχή μπορεί να έχει αλλάξει μετά το await — ελέγξτε ξανά
cache[id] = result
return result
}
}
Οι πραγματοποιητές πρέπει να λαμβάνουν υπόψη την reentrancy και να ελέγχουν την κατάσταση του actor μετά από σημεία await. Ένα τυπικό λάθος είναι η υπόθεση ότι η απομόνωση actor διατηρείται μέσω ασύγχρονων παύσεων. Στην πράξη, μεταξύ await και της επόμενης εντολής, η κατάσταση μπορεί να διαφέρει από την αναμενόμενη.
Συχνές Ερωτήσεις
Actor απομονώνει αυτόματα την κατάστασή του από συνθήκες ανταγωνισμού, απαιτώντας await για πρόσβαση από εξωτερική. Η κλάση δεν παρέχει τέτοια προστασία — ο πραγματοποιητής είναι υπεύθυνος για τη συγχρόνιση μέσω κλειδωμάτων ή ουρών. Ο actor κληρονομεί μόνο από actor, η κλάση από κλάση.
Ναί, ένας actor μπορεί να κληρονομήσει από άλλον actor. Η υποκλάση λαμβάνει όλες τις απομονωμένες ιδιότητες και μεθόδους του γονέα. Ο actor δεν μπορεί να κληρονομήσει από κλάση, επειδή οι κλάσεις δεν έχουν μηχανισμό απομόνωσης actor σε επίπεδο μεταγλωττιστή.
Actor-isolated περιεχόμενο — είναι η περιοχή κώδικα όπου επιτρέπεται απευθείας πρόσβαση στην μεταβλητή κατάσταση του actor. Μέσα σε μεθόδους actor που έχουν σημειωθεί ως isolated (από προεπιλογή), μπορείτε να διαβάσετε και να γράψετε ιδιότητες χωρίς await. Ο μεταγλωττιστής ελέγχει τα όρια απομόνωσης.
Τα δεδομένα από τον actor μεταφέρονται μέσω async-μεθόδων που επιστρέφουν τύπους Sendable, ή μέσω nonisolated μεθόδων που δέχονται τιμές ως παραμέτρους. Μπορείτε επίσης να δημιουργήσετε μια async ιδιότητα που επιστρέφει ένα στιγμιότυπο της κατάστασης του actor σε μορφή Sendable δομής.
Ναί, ο actor μπορεί να συμμορφωθεί με πρωτόκολλα. Αν το πρωτόκολλο περιέχει απομονωμένες απαιτήσεις (actor-isolated), αυτές γίνονται αυτόματα actor-απομονωμένες. Για ασύγχρονες μεθόδους σε πρωτόκολλα, μπορείτε να καθορίσετε με το σημαδοτέτη isolated ότι πρέπει να κληθούν σε συγκεκριμένο actor.
Συνοψή
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης