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

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

Delegate — είναι ένα μοτίβο σχεδίασης κατά το οποίο ένα αντικείμενο αναθέτει την εκτέλεση μιας εργασίας σε άλλο αντικείμενο. Στο iOS, το μοτίβο υλοποιείται μέσω πρωτοκόλλων Swift και @protocol Objective-C. Η Delegation είναι ένα από τα θεμελιώδη μοτίβα του Cocoa Touch, που χρησιμοποιείται σε UITableViewDelegate, UITextFieldDelegate και εκατοντάδες άλλα API της Apple. Σύμφωνα με την Apple Developer Documentation (2025), περίπου το 70% των κλάσεων συστήματος του UIKit χρησιμοποιούν αντιπροσώπους για προσαρμογή συμπεριφοράς χωρίς κληρονομικότητα.

Κύρια σημεία

  • Delegate — μοτίβο όπου το αντικείμενο Α αναθέτει την εκτέλεση της εργασίας στο αντικείμενο Β μέσω πρωτοκόλλου ή διεπαφής
  • Πρωτόκολλο Swift ορίζει ένα σύνολο μεθόδων που ο αντιπρόσωπος μπορεί ή πρέπει να υλοποιήσει
  • Weak αναφορά στον αντιπρόσωπο είναι υποχρεωτική για την πρόληψη retain cycle και διαρροών μνήμης
  • UITableViewDelegate — το πιο διαδεδομένο παράδειγμα αντιπροσώπου στο iOS με 20+ προαιρετικές μεθόδους
  • Διαφορά από Observer: ο αντιπρόσωπος είναι σχέση 1:1, ενώ οι ειδοποιήσεις λειτουργούν με βάση την αρχή 1:N

Τι είναι το μοτίβο Delegate;

Delegate — είναι ένα μοτίβο σχεδίασης συμπεριφοράς που επιτρέπει σε ένα αντικείμενο να μεταφέρει μέρος των ευθυνών του σε άλλο αντικείμενο. Σε αντίθεση με την κληρονομικότητα, όπου η θυγατρική κλάση παρακάμπτει τις μεθόδους του γονέα, ο αντιπρόσωπος χρησιμοποιεί σύνθεση: το αντικείμενο-ιδιοκτήτης αποθηκεύει μια αναφορά στον αντιπρόσωπο και καλεί τις μεθόδους του σε συγκεκριμένα σημεία.

Ο αντιπρόσωπος ορίζει ένα πρωτόκολλο — ένα σύνολο μεθόδων που ο αντιπρόσωπος μπορεί να υλοποιήσει. Οι μέθοδοι χωρίζονται σε υποχρεωτικές (required) και προαιρετικές (optional). Στο Swift, οι προαιρετικές μέθοδοι πρωτοκόλλου επισημαίνονται με τη λέξη-κλειδί @objc optional.

Αρχή λειτουργίας του αντιπροσώπου

Το αντικείμενο Α (ιδιοκτήτης) περιέχει την ιδιότητα delegate — μια weak αναφορά στο αντικείμενο Β (αντιπρόσωπος). Όταν συμβαίνει ένα γεγονός, το Α ελέγχει αν το Β υλοποιεί την αντίστοιχη μέθοδο πρωτοκόλλου και την καλεί. Η weak αναφορά είναι υποχρεωτική: χωρίς αυτήν, ο αντιπρόσωπος δεν μπορεί να απελευθερωθεί από τη μνήμη, επειδή ο ιδιοκτήτης τον κρατά με ισχυρή αναφορά.

Swift
protocol LoaderDelegate: AnyObject {
    func loaderDidStart(_ loader: DataLoader)
    func loader(_ loader: DataLoader, didLoad data: Data)
    func loader(_ loader: DataLoader, didFailWith error: Error)
}

class DataLoader {
    weak var delegate: LoaderDelegate?
    
    func start() {
        delegate?.loaderDidStart(self)
        // ασύγχρονη φόρτωση
    }
}

Η κλάση DataLoader ορίζει το πρωτόκολλο LoaderDelegate και καλεί τις μεθόδους του αντιπροσώπου σε βασικά σημεία του κύκλου ζωής φόρτωσης. Το AnyObject εγγυάται ότι το πρωτόκολλο μπορεί να υλοποιηθεί μόνο από κλάσεις — αυτό είναι απαραίτητο για την weak αναφορά.

Πώς λειτουργεί το Delegate στο Swift;

Η υλοποίηση του αντιπροσώπου στο Swift περιλαμβάνει τρία βήματα: δήλωση του πρωτοκόλλου, δημιουργία της weak ιδιότητας delegate στον ιδιοκτήτη και υλοποίηση του πρωτοκόλλου στην κλάση-αντιπρόσωπο. Ας το εξετάσουμε στο παράδειγμα ενός προσαρμοσμένου UITextField με επικύρωση.

Swift
protocol ValidatorDelegate: AnyObject {
    func validate(_ input: String) -> Bool
    func validatorDidFail(_ input: String)
}

class ValidatedTextField: UITextField {
    weak var validator: ValidatorDelegate?
    
    override func textDidChange() {
        guard let text = self.text else { return }
        if validator?.validate(text) == false {
            validator?.validatorDidFail(text)
            self.layer.borderColor = UIColor.red.cgColor
        }
    }
}

class LoginViewController: UIViewController, ValidatorDelegate {
    let textField = ValidatedTextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.validator = self
    }
    
    func validate(_ input: String) -> Bool {
        return input.count >= 6
    }
    
    func validatorDidFail(_ input: String) {
        print("Η επικύρωση απέτυχε: η είσοδος είναι πολύ σύντομη")
    }
}

Το LoginViewController υλοποιεί το πρωτόκολλο ValidatorDelegate και ορίζει τον εαυτό του ως αντιπρόσωπο του textField. Σε κάθε αλλαγή κειμένου, το ValidatedTextField καλεί τη validate(_:), και αν η επικύρωση αποτύχει — validatorDidFail(_:). Ο Controller λειτουργεί ως μεσάζων μεταξύ του View και της λογικής επικύρωσης.

Delegate vs NotificationCenter vs Closure

Η επιλογή μεταξύ αντιπροσώπου, ειδοποιήσεων και closures εξαρτάται από τον αριθμό των παραληπτών και τον βαθμό σύζευξης των στοιχείων. Κάθε μηχανισμός λύνει το πρόβλημα επικοινωνίας μεταξύ αντικειμένων, αλλά με διαφορετικούς συμβιβασμούς.

ΧαρακτηριστικόDelegateNotificationCenterClosure
Τύπος σύνδεσης1:11:N1:1
ΣύζευξηΑσθενής (μέσω πρωτοκόλλου)Πολύ ασθενής (κλειδί-συμβολοσειρά)Μέτρια (σύλληψη περιβάλλοντος)
Ασφάλεια τύπωνΠλήρηςΑπουσιάζει (Any?)Πλήρης
Κίνδυνος retain cycleΌχι (weak)ΌχιΝαι (σύλληψη self)
Πότε να εφαρμόζεταιΣύνθετα callback με πολλαπλές μεθόδουςΓεγονότα που ενδιαφέρουν πολλούςΑπλά closures με 1-2 callback

Delegate είναι βέλτιστο όταν πρέπει να σταλεί μια σειρά σχετικών γεγονότων σε έναν παραλήπτη. NotificationCenter είναι καλύτερο για ειδοποιήσεις broadcast. Closure — για απλές ασύγχρονες λειτουργίες, όπως το completion handler στο URLSession.

Υλοποίηση Delegate σε Objective-C

Objective-C χρησιμοποιεί @protocol και @optional για τη δήλωση αντιπροσώπων. Σε αντίθεση με το Swift, όλες οι μέθοδοι του πρωτοκόλλου είναι προαιρετικές από προεπιλογή. Η βασική διαφορά — η κλήση respondsToSelector: πριν από την αποστολή μηνύματος στον αντιπρόσωπο, καθώς η μέθοδος μπορεί να μην έχει υλοποιηθεί.

Objective-C
@protocol ImageCacheDelegate 
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end

@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end

@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
    if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
        [self.delegate cacheDidStartDownload:self];
    }
    // ασύγχρονη φόρτωση εικόνας
}
@end

Η βασική διαφορά στο Objective-C: πριν από την κλήση μιας προαιρετικής μεθόδου απαιτείται έλεγχος respondsToSelector:. Στο Swift, οι προαιρετικές μέθοδοι πρωτοκόλλου εξαλείφουν αυτόν τον έλεγχο — το optional chaining (?.) χειρίζεται αυτόματα την περίπτωση απουσίας υλοποίησης.

Τυπικά λάθη κατά την εργασία με αντιπροσώπους

Λάθη χρήσης αντιπροσώπων οδηγούν σε διαρροές μνήμης, σφαλματικές καταρρεύσεις της εφαρμογής και μη προφανή σφάλματα. Ας εξετάσουμε πέντε συνηθισμένα προβλήματα.

Ισχυρή αναφορά στον αντιπρόσωπο

Retain cycle — το πιο συνηθισμένο σφάλμα. Εάν η ιδιότητα delegate δηλωθεί ως strong, και ο αντιπρόσωπος με τη σειρά του κατέχει το αντικείμενο-ιδιοκτήτη, δημιουργείται ένας κύκλος διατήρησης. Και τα δύο αντικείμενα δεν θα απελευθερωθούν ποτέ από τη μνήμη. Λύση: δηλώνετε πάντα τον αντιπρόσωπο ως weak var στο Swift ή @property (weak) στο Objective-C.

Ο αντιπρόσωπος δεν μηδενίστηκε μετά το dealloc

Εάν το αντικείμενο-ιδιοκτήτης επιζήσει του αντιπροσώπου και η αναφορά παραμείνει, η κλήση της μεθόδου του αντιπροσώπου θα οδηγήσει σε EXC_BAD_ACCESS. Η weak αναφορά λύνει αυτό το πρόβλημα αυτόματα: μετά την απελευθέρωση του αντιπροσώπου, η ιδιότητα γίνεται nil. Ωστόσο, σε σενάρια πολλαπλών νημάτων, αξίζει να ελέγχετε τον αντιπρόσωπο και στο κύριο νήμα.

Υπερβολικός αριθμός μεθόδων στο πρωτόκολλο

Ένα πρωτόκολλο με 20+ μεθόδους παραβιάζει την αρχή διαχωρισμού διεπαφών (ISP). Το UITableViewDelegate περιέχει περίπου 30 προαιρετικές μεθόδους — αυτή είναι μια ιστορική εξαίρεση. Στα δικά σας πρωτόκολλα, είναι καλύτερο να διαιρέσετε την ευθύνη σε πολλά μικρά πρωτόκολλα, το καθένα με τον δικό του ρόλο.

Παραδείγματα Delegate σε API συστήματος iOS

Τα API συστήματος της Apple χρησιμοποιούν ενεργά το μοτίβο Delegate. Ας εξετάσουμε τρία βασικά παραδείγματα από το UIKit που εμφανίζονται σε κάθε εφαρμογή iOS.

APIΠρωτόκολλοΒασικές μέθοδοι
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

Κάθε ένα από αυτά τα πρωτόκολλα υλοποιεί διαφορετικές πτυχές συμπεριφοράς: UITableViewDelegate διαχειρίζεται την εμφάνιση και την αντίδραση στο άγγιγμα, UITextFieldDelegate ελέγχει την εισαγωγή κειμένου, URLSessionDelegate επεξεργάζεται γεγονότα δικτύου. Αυτό δείχνει την ευελιξία του μοτίβου: ο αντιπρόσωπος μπορεί να προσαρμοστεί σε οποιονδήποτε τομέα ευθύνης.

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

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

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

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

Η weak αναφορά αποτρέπει το retain cycle. Ο ιδιοκτήτης (π.χ. UITableView) κρατά την αναφορά στον αντιπρόσωπο μόνο ως weak. Εάν ο αντιπρόσωπος (UIViewController) κατέχει τον πίνακα, μια ισχυρή αναφορά στον αντιπρόσωπο θα δημιουργούσε έναν κύκλο: ViewController → UITableView → Delegate (ViewController). Η Weak σπάει αυτόν τον κύκλο.

Μπορεί να χρησιμοποιηθεί delegate στο SwiftUI;

Στο SwiftUI, το μοτίβο Delegate χρησιμοποιείται λιγότερο συχνά — αντικαθίσταται από @Binding, @State και closures. Ωστόσο, το delegate εξακολουθεί να χρησιμοποιείται για την ενσωμάτωση UIKit μέσω UIViewRepresentable. Για παράδειγμα, τα MKMapViewDelegate και WKUIDelegate παραμένουν επίκαιρα κατά την περιτύλιξη στοιχείων UIKit σε SwiftUI.

Τι είναι το @objc optional στα πρωτόκολλα Swift;

@objc optional επιτρέπει τη δήλωση προαιρετικών μεθόδων σε ένα πρωτόκολλο Swift. Είναι ένας μηχανισμός συμβατότητας με το Objective-C runtime. Χωρίς @objc, όλες οι μέθοδοι ενός πρωτοκόλλου Swift είναι υποχρεωτικές από προεπιλογή. Το Optional χρησιμοποιείται σε πρωτόκολλα UIKit, όπου ο αντιπρόσωπος μπορεί να υλοποιήσει μόνο τις μεθόδους που χρειάζεται.

Πόσους αντιπροσώπους μπορεί να έχει ένα αντικείμενο;

Ένα αντικείμενο μπορεί να έχει μόνο έναν αντιπρόσωπο για κάθε ιδιότητα delegate. Εάν πρέπει να ειδοποιηθούν πολλά αντικείμενα, χρησιμοποιήστε multicast delegate, μια συστοιχία αντιπροσώπων ή NotificationCenter. Το μοτίβο Delegate έχει σχεδιαστεί αρχικά ως σχέση 1:1.

Σύνοψη

  • Delegate — μοτίβο σχεδίασης συμπεριφοράς που μεταφέρει την εκτέλεση μιας εργασίας από το ένα αντικείμενο στο άλλο μέσω πρωτοκόλλου
  • Πρωτόκολλο Swift δηλώνει ένα σύνολο μεθόδων; οι προαιρετικές μέθοδοι επισημαίνονται με @objc optional, οι υποχρεωτικές υλοποιούνται πάντα
  • Weak αναφορά στον αντιπρόσωπο είναι υποχρεωτική για την πρόληψη retain cycle και διαρροών μνήμης
  • UITableViewDelegate — το πιο γνωστό παράδειγμα; οι μέθοδοί του διαχειρίζονται το ύψος, την επιλογή και την εμφάνιση κελιών
  • Delegate διαφέρει από το NotificationCenter στη σχέση 1:1 έναντι 1:N και στην πλήρη ασφάλεια τύπων
  • Objective-C απαιτεί έλεγχο respondsToSelector: για προαιρετικές μεθόδους, το Swift το χειρίζεται αυτόματα
  • Σύσταση: χρησιμοποιήστε delegate για σύνθετα callback με πολλές σχετικές μεθόδους και closure για απλά κλεισίματα

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

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

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

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