Delegate: τι είναι, μοτίβο αντιπροσώπου και πώς λειτουργεί στο iOS

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-17 Χρόνος ανάγνωσης: 10 λεπ

Delegate — είναι ένα μοτίβο σχεδίασης στο οποίο ένα αντικείμενο αναθέτει την εκτέλεση εργασιών σε άλλο αντικείμενο μέσω ενός πρωτοκόλλου με προκαθορισμένες μεθόδους. Στην ανάπτυξη iOS, το Delegate είναι ένα από τα θεμελιώδη μοτίβα του Cocoa Touch, που χρησιμοποιείται για ασύγχρονη ειδοποίηση χωρίς άμεση σύνδεση μεταξύ αποστολέα και παραλήπτη. Σύμφωνα με το Apple Documentation (2025), η αντιπροσώπευση εφαρμόζεται στα Foundation και UIKit για την επεξεργασία γεγονότων πινάκων, αιτημάτων δικτύου και διαχείρισης τοποθεσίας. Το μοτίβο εξασφαλίζει χαλαρή σύζευξη στοιχείων και επαναχρησιμοποίηση κώδικα.

Βασικά σημεία

  • Delegate — μοτίβο στο οποίο το αντικείμενο αναθέτει την επεξεργασία γεγονότων σε άλλο αντικείμενο μέσω πρωτοκόλλου.
  • Protocol στη Swift ορίζει το σύνολο μεθόδων που ο delegate πρέπει ή μπορεί να υλοποιήσει.
  • Weak reference είναι υποχρεωτική για την ιδιότητα delegate για να αποφευχθεί ο retain cycle.
  • @objc optional επιτρέπει να γίνουν οι μέθοδοι πρωτοκόλλου προαιρετικές για υλοποίηση.
  • URLSessionDelegate — ασύγχρονος delegate για επεξεργασία γεγονότων αιτήματος δικτύου.

Τι είναι το Delegate;

Delegate (αντιπρόσωπος) — είναι ένα αντικείμενο που υλοποιεί ένα συγκεκριμένο πρωτόκολλο και λαμβάνει ειδοποιήσεις για γεγονότα ενός άλλου αντικειμένου. Το μοτίβο Delegation είναι μια εναλλακτική της κληρονομικότητας: αντί να δημιουργεί μια υποκλάση για την παράκαμψη μεθόδων, το αντικείμενο αναθέτει την επεξεργασία γεγονότων σε ένα εξωτερικό αντικείμενο. Στο iOS, η delegation υλοποιείται μέσω πρωτοκόλλων Swift με υποχρεωτικές και προαιρετικές μεθόδους. Η ιδιότητα delegate δηλώνεται πάντα ως weak var για να αποφευχθούν κυκλικές αναφορές μεταξύ αντικειμένων.

Ορισμός πρωτοκόλλου Delegate

Το πρωτόκολλο delegate ορίζει τη σύμβαση αλληλεπίδρασης μεταξύ αντικειμένων. Υποχρεωτικές μέθοδοι πρέπει να υλοποιηθούν από τον αντιπρόσωπο, διαφορετικά ο κώδικας δεν θα μεταγλωττιστεί. Προαιρετικές μέθοδοι επισημαίνονται με το χαρακτηριστικό @objc optional και επιτρέπουν στον αντιπρόσωπο να αντιδρά μόνο στα απαραίτητα γεγονότα. Τα ονόματα μεθόδων ακολουθούν τη σύμβαση: πρώτη παράμετρος — το αντικείμενο-αποστολέας, δεύτερη — τα δεδομένα γεγονότος. Για παράδειγμα, το tableView(_:didSelectRowAt:) υποδεικνύει ότι ο αποστολέας είναι UITableView και τα δεδομένα είναι το ευρετήριο της επιλεγμένης γραμμής.

swift
// Πρωτόκολλο Delegate
protocol DownloadManagerDelegate: AnyObject {
    func downloadManager(_ manager: DownloadManager,
                          didFinishWith data: Data)
    func downloadManager(_ manager: DownloadManager,
                          didFailWith error: Error)

    @objc optional func downloadManager(_ manager: DownloadManager,
                                didUpdateProgress progress: Float)
}

// Κλάση που χρησιμοποιεί Delegate
class DownloadManager {
    weak var delegate: DownloadManagerDelegate?

    func startDownload(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let self else { return }
            if let error = error {
                self.delegate?.downloadManager(self, didFailWith: error)
            } else if let data = data {
                self.delegate?.downloadManager(self, didFinishWith: data)
            }
        }.resume()
    }
}

Ασθενής αναφορά στο delegate

Η ιδιότητα delegate πρέπει να δηλωθεί ως weak var (ασθενής δείκτης) για την αποφυγή retain cycle. Εάν η αναφορά ήταν ισχυρή, ο αντιπρόσωπος και το αντικείμενο αντιπροσώπευσης θα κρατούσαν το ένα το άλλο και η ARC δεν θα μπορούσε να ελευθερώσει τη μνήμη τους. Τα πρωτόκολλα delegate κληρονομούν το AnyObject (μόνο για κλάσεις), το οποίο επιτρέπει τη χρήση weak. Οι δομές (struct) και τα enum δεν μπορούν να είναι αντιπρόσωποι λόγω της σημασιολογίας τιμών. Εναλλακτική για value types — callback closures.

swift
class ViewController: DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — κανένας retain cycle
        manager.startDownload(from: url)
    }

    func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
        processData(data)
    }

    func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
        showError(error)
    }
}

Πώς λειτουργεί το μοτίβο Delegate στο iOS;

Το μοτίβο Delegate λειτουργεί με την αρχή “ένας-προς-έναν”: ένα αντικείμενο-αποστολέας μπορεί να έχει μόνο έναν αντιπρόσωπο σε μια συγκεκριμένη χρονική στιγμή. Όταν συμβαίνει ένα γεγονός, ο αποστολέας ελέγχει αν έχει οριστεί ο delegate και καλεί την αντίστοιχη μέθοδο του πρωτοκόλλου. Πλεονέκτημα έναντι της άμεσης κλήσης — ο αποστολέας δεν γνωρίζει τον τύπο του αντιπροσώπου, μόνο ότι αντιστοιχεί στο πρωτόκολλο. Αυτό συμμορφώνεται με την αρχή αντιστροφής εξάρτησης (DIP) από το SOLID.

Κύκλος ζωής του αντιπροσώπου

Ο αντιπρόσωπος ορίζεται μέσω ανάθεσης: someObject.delegate = self. Κατά την αποδέσμευση του αντιπροσώπου, η ιδιότητα γίνεται αυτόματα nil λόγω της σημασιολογίας weak. Πριν από την κλήση της μεθόδου delegate, γίνεται έλεγχος μέσω optional chaining: delegate?.method(). Αν ο delegate είναι nil — η κλήση αγνοείται χωρίς crash. Για τις προαιρετικές μεθόδους πρωτοκόλλου χρησιμοποιείται επιπλέον έλεγχος: delegate?.responds(to: #selector(...)), αν και στη Swift αυτός ο έλεγχος είναι συνήθως σιωπηρός μέσω της δήλωσης προαιρετικής μεθόδου.

Ασύγχρονη ειδοποίηση μέσω Delegate

Σε περιβάλλον πολλαπλών νημάτων, ο delegate χρησιμοποιείται για ασύγχρονη επιστροφή αποτελέσματος. URLSession παρέχει URLSessionDelegate με μεθόδους που καλούνται κατά τη λήψη δεδομένων, timeout ή σφάλμα αυθεντικοποίησης. Οι μέθοδοι delegate εκτελούνται στην ουρά παρασκηνίου του URLSession, επομένως απαιτείται δρομολόγηση στο main queue για ενημερώσεις UI. Ο ασύγχρονος delegate δεν μπλοκάρει το νήμα κλήσης, επιτρέποντας τη συνέχιση άλλων εργασιών.

swift
class NetworkService: NSObject, URLSessionDataDelegate {
    private lazy var session = URLSession(
        configuration: .default,
        delegate: self,
        delegateQueue: OperationQueue()
    )
    private var receivedData = Data()

    func urlSession(_ session: URLSession,
                    dataTask: URLSessionDataTask,
                    didReceive data: Data) {
        receivedData.append(data)
        let progress = Float(receivedData.count) / Float(expectedSize)
        DispatchQueue.main.async {
            self.progressHandler?(progress)
        }
    }

    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didCompleteWithError error: Error?) {
        if let error = error {
            delegate?.networkService(self, didFailWith: error)
        } else {
            delegate?.networkService(self, didReceive: receivedData)
        }
    }
}

Delegate vs Callback: σύγκριση προσεγγίσεων

Delegate και Callback λύνουν την ίδια εργασία — ασύγχρονη ειδοποίηση — αλλά με διαφορετικούς τρόπους. Ο Delegate χρησιμοποιεί πρωτόκολλο με ονομασμένες μεθόδους, το callback — closure με καταγραφή περιβάλλοντος. Η επιλογή εξαρτάται από τον αριθμό γεγονότων, την πολυπλοκότητα υπογραφών και αρχιτεκτονικές προτιμήσεις. Η Apple συνιστά τον delegate για API με πολλαπλά γεγονότα (UITableView — 20+ μέθοδοι) και το callback για εφάπαξ ολοκληρώσεις.

Πότε υπερτερεί ο Delegate

Ο Delegate είναι προτιμότερος όταν πρέπει να επεξεργαστούν πολλαπλά διαφορετικά γεγονότα από μία πηγή. Για παράδειγμα, το CLLocationManager ειδοποιεί τον αντιπρόσωπο για αλλαγή τοποθεσίας, σφάλματα αδειών, είσοδο/έξοδο από γεωζώνες και αλλαγή κατάστασης υπηρεσιών. Κάθε γεγονός είναι μια ξεχωριστή μέθοδος πρωτοκόλλου με σαφές όνομα και τυποποιημένες παραμέτρους. Ο Delegate είναι επίσης βολικός για διαμόρφωση συμπεριφοράς (μέθοδοι should, will, did).

Πότε υπερτερεί το Callback

Το Callback είναι απλούστερο για εφάπαξ αιτήματα με ένα αποτέλεσμα. Completion handler στο URLSession.dataTask καταλαμβάνει μία γραμμή στο σημείο κλήσης έναντι τουλάχιστον τριών μεθόδων πρωτοκόλλου. Το Callback είναι επίσης πιο φυσικό για λειτουργικές αλυσίδες (map, flatMap, async/await). Ωστόσο, σε ένθεση άνω των 2-3 επιπέδων, το callback μετατρέπεται σε Callback Hell, ενώ ο delegate παραμένει πάντα επίπεδος.

Ενσωματωμένοι Delegates στο iOS SDK

Το iOS SDK περιέχει δεκάδες ενσωματωμένα πρωτόκολλα delegate για διάφορα υποσυστήματα. Κάθε ένα έχει σχεδιαστεί για ένα συγκεκριμένο σενάριο αλληλεπίδρασης. Σύμφωνα με το Apple Documentation (2025), οι πιο συχνά χρησιμοποιούμενοι delegates είναι UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate και UNUserNotificationCenterDelegate. Αυτά τα πρωτόκολλα περιέχουν 3 έως 30 μεθόδους με διαφορετικό βαθμό υποχρεωτικότητας.

UITableViewDelegate

UITableViewDelegate διαχειρίζεται την εμφάνιση και συμπεριφορά κελιών πίνακα. Περιέχει μεθόδους για επεξεργασία επιλογής γραμμής, ρύθμιση ύψους κελιών, προσαρμοσμένες προβολές header/footer και ενέργειες ολίσθησης. Όλες οι μέθοδοι του πρωτοκόλλου είναι προαιρετικές, επιτρέποντας την υλοποίηση μόνο της απαραίτητης λειτουργικότητας. Απουσία αντιπροσώπου, ο πίνακας λειτουργεί με προεπιλεγμένες ρυθμίσεις. Ιστορικά, ο delegate συνδυαζόταν με το UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate παρέχει λεπτομερή έλεγχο επί των αιτημάτων HTTP. Οι μέθοδοι του αντιπροσώπου καλούνται κατά τη λήψη απόκρισης διακομιστή, δεδομένων, ολοκλήρωσης λήψης. Τα εξειδικευμένα υποπρωτόκολλα URLSessionTaskDelegate και URLSessionDataDelegate επεκτείνουν τη βασική λειτουργικότητα για συγκεκριμένους τύπους εργασιών. Ο Delegate απαιτείται για υποστήριξη λήψεων παρασκηνίου, πιστοποιητικών SSL και προσαρμοσμένης επεξεργασίας ανακατευθύνσεων.

DelegateΜέθοδοιΣκοπός
UITableViewDelegate25Εμφάνιση και αλληλεπίδραση με πίνακα
UITextFieldDelegate8Επεξεργασία εισαγωγής κειμένου και πληκτρολογίου
CLLocationManagerDelegate12Ενημέρωση τοποθεσίας και γεωζώνες
URLSessionDelegate6Διαχείριση περιόδου HTTP και πιστοποιητικών
UNUserNotificationCenterDelegate4Επεξεργασία push ειδοποιήσεων σε πρώτο πλάνο

Διαχείριση μνήμης κατά την εργασία με Delegate

Η διαχείριση μνήμης είναι μια κρίσιμη πτυχή της εργασίας με τον delegate στο iOS. ARC (Automatic Reference Counting) διαχειρίζεται αυτόματα τη μνήμη, αλλά μόνο με σωστή χρήση weak/unowned αναφορών. Η παραβίαση κανόνων οδηγεί σε διαρροές μνήμης ή πρόωρη αποδέσμευση. Ο delegate δηλωμένος ως strong δημιουργεί retain cycle αν ο κάτοχος του αντιπροσώπου διατηρεί επίσης αναφορά στο αντικείμενο αντιπροσώπευσης.

Retain cycle μέσω Delegate

Retain cycle προκύπτει όταν το αντικείμενο A (κάτοχος) ορίζει τον εαυτό του ως αντιπρόσωπο του αντικειμένου B, και το B διατηρεί ισχυρή αναφορά στον delegate. Παράδειγμα: το ViewController δημιουργεί μια URLSession, ορίζει το self ως αντιπρόσωπο της περιόδου, αλλά η URLSession από προεπιλογή διατηρεί ισχυρή αναφορά στον delegate αν δεν έχει καθοριστεί delegateQueue. Λύση — ελέγχετε πάντα την τεκμηρίωση API για τον τύπο αναφοράς στον delegate (weak ή strong) και μηδενίστε ρητά τον delegate στο deinit.

swift
class SafeViewController: UIViewController {
    private var session: URLSession?
    private var service: NetworkService?

    override func viewDidLoad() {
        super.viewDidLoad()
        service = NetworkService()
        service?.delegate = self
    }

    deinit {
        // Μηδενισμός delegate στο deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession με weak delegate μέσω NSObject
class WeakDelegateSession: NSObject {
    private weak var delegate: URLSessionDelegate?

    func createSession() -> URLSession {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        return URLSession(
            configuration: .default,
            delegate: self,
            delegateQueue: queue
        )
    }
}

Ασφαλής έλεγχος delegate πριν από την κλήση

Πριν από την κλήση της μεθόδου του αντιπροσώπου, πρέπει να ελεγχθεί ότι ο αντιπρόσωπος υπάρχει (δεν είναι nil) και υλοποιεί την καλούμενη μέθοδο. Για υποχρεωτικές μεθόδους πρωτοκόλλου, δεν απαιτείται έλεγχος — ο μεταγλωττιστής εγγυάται την υλοποίηση. Για προαιρετικές μεθόδους χρησιμοποιήστε respond(to:) ή optional chaining. Εάν ο αντιπρόσωπος έχει αποδεσμευτεί, η weak αναφορά γίνεται αυτόματα nil και η κλήση του αντιπροσώπου αγνοείται. Αυτή είναι ασφαλής συμπεριφορά που δεν απαιτεί πρόσθετη επεξεργασία.

Τυπικά λάθη κατά την υλοποίηση του Delegate

Οι προγραμματιστές συχνά κάνουν λάθη κατά την εργασία με το μοτίβο Delegate, ιδιαίτερα στα αρχικά στάδια εκμάθησης του iOS. Πιο συνηθισμένα: retain cycle λόγω strong delegate, λήθη κλήσης delegate?.method(), λανθασμένη υπογραφή μεθόδων πρωτοκόλλου, ορισμός delegate μετά την έναρξη λειτουργίας και συγκρούσεις πολλαπλών νημάτων. Ας εξετάσουμε κάθε λάθος και τρόπους πρόληψής του.

Strong reference αντί weak

Το πιο κρίσιμο λάθος — δήλωση της ιδιότητας delegate ως strong var αντί weak var. Αυτό δημιουργεί ένα retain cycle όπου ούτε ο αντιπρόσωπος ούτε το αντικείμενο αντιπροσώπευσης μπορούν να ελευθερωθούν. Συνέπειες: διαρροή μνήμης, επιβράδυνση εφαρμογής και κρυμμένα σφάλματα. Λύση: χρησιμοποιείτε πάντα weak var για τον delegate και κληρονομήστε το πρωτόκολλο από AnyObject για να αποκλειστεί η χρήση value types ως αντιπροσώπου.

Ορισμός delegate μετά την έναρξη λειτουργίας

Εάν ο delegate ορίζεται μετά την κλήση μιας ασύγχρονης μεθόδου, τα πρώτα γεγονότα μπορεί να χαθούν. Παράδειγμα: κλήση startDownload() πριν από την ανάθεση manager.delegate = self οδηγεί σε απώλεια callback ολοκλήρωσης, εάν η λήψη εκτελείται σύγχρονα ή πολύ γρήγορα. Λύση: ορίστε τον delegate πριν από την κλήση της ασύγχρονης μεθόδου και τεκμηριώστε τη σειρά αρχικοποίησης στα σχόλια του πρωτοκόλλου.

Συχνές ερωτήσεις

Γιατί ο delegate δηλώνεται ως weak;

Weak αποτρέπει τον retain cycle μεταξύ του αντιπροσώπου και του αντικειμένου αντιπροσώπευσης. Εάν η αναφορά ήταν strong, τα αντικείμενα θα κρατούσαν το ένα το άλλο και η ARC δεν θα μπορούσε να τα ελευθερώσει. Η weak-αναφορά γίνεται αυτόματα nil κατά την αποδέσμευση του αντιπροσώπου. Αυτή είναι τυπική πρακτική του Cocoa Touch από την εμφάνιση του Objective-C και διατηρήθηκε στη Swift για συμβατότητα προς τα πίσω.

Ποια είναι η διαφορά μεταξύ delegate και dataSource;

Delegate επεξεργάζεται γεγονότα και διαχειρίζεται συμπεριφορά (ύψος κελιών, αντίδραση σε κλικ). DataSource παρέχει δεδομένα για εμφάνιση (αριθμός γραμμών, κελιά). Ο Delegate απαντά στην ερώτηση “πώς;”, το dataSource — στην ερώτηση “τι;”. Στο iOS, και τα δύο υλοποιούνται μέσω πρωτοκόλλων, συχνά στον ίδιο ελεγκτή, αλλά εννοιολογικά διαχωρίζονται.

Μπορεί να χρησιμοποιηθεί struct ως delegate;

Όχι, εάν το πρωτόκολλο κληρονομεί το AnyObject (πρωτόκολλο κλάσης). Οι weak-αναφορές είναι διαθέσιμες μόνο για reference types (κλάσεις). Για value types (struct, enum) χρησιμοποιήστε callback closures ή ξεχωριστή κλάση περιτυλίγματος. Εάν ελέγχετε το πρωτόκολλο, μπορείτε να μην κληρονομήσετε AnyObject, αλλά τότε το weak απαγορεύεται — επιλέξτε συνειδητά μεταξύ weak-delegate και struct-αντιπροσώπου.

Τι είναι η μέθοδος responds(to:) και σε τι χρησιμεύει;

responds(to:) — μέθοδος του NSObjectProtocol που ελέγχει αν το αντικείμενο υλοποιεί τον καθορισμένο selector. Χρησιμοποιείται για έλεγχο προαιρετικών @objc μεθόδων πρωτοκόλλου πριν από την κλήση. Χωρίς αυτόν τον έλεγχο, η κλήση μιας μη υλοποιημένης προαιρετικής μεθόδου θα οδηγήσει σε NSInvalidArgumentException. Στη Swift για πρωτόκολλα με @objc optional, ο έλεγχος μπορεί να είναι σιωπηρός μέσω optional binding.

Είναι ο Delegate singleton;

Όχι, ο delegate είναι ένα μοτίβο αντιπροσώπευσης, όχι singleton. Σε αντίθεση με το singleton, ο αντιπρόσωπος μπορεί να αντικατασταθεί κατά το runtime και υπάρχει σε ένα στιγμιότυπο για κάθε αντικείμενο αντιπροσώπευσης. Ένα αντικείμενο μπορεί να είναι αντιπρόσωπος για πολλούς αποστολείς. Το Singleton είναι ένα δημιουργικό μοτίβο που εγγυάται ένα μοναδικό στιγμιότυπο κλάσης και δεν έχει καμία σχέση με την αντιπροσώπευση.

Σύνοψη

  • Delegate — μοτίβο στο οποίο το αντικείμενο αναθέτει την επεξεργασία γεγονότων σε άλλο αντικείμενο μέσω πρωτοκόλλου με τυποποιημένες μεθόδους.
  • Weak var είναι υποχρεωτικό για την ιδιότητα delegate για την αποφυγή retain cycle και διαρροών μνήμης.
  • Πρωτόκολλο ορίζει τις υποχρεωτικές και προαιρετικές (@objc optional) μεθόδους αντιπροσώπευσης.
  • iOS SDK περιέχει 15+ ενσωματωμένα πρωτόκολλα delegate: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate είναι προτιμότερος από το callback σε 3+ διαφορετικά γεγονότα από μία πηγή (CLLocationManager).
  • Ασύγχρονος delegate χρησιμοποιείται στο URLSession για λήψη δεδομένων και προόδου χωρίς μπλοκάρισμα νήματος.
  • Ορίστε τον delegate πριν από την έναρξη ασύγχρονης λειτουργίας και μηδενίστε τον στο deinit για ασφαλή διαχείριση μνήμης.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης