Στο Swift, τα closures ορίζονται ως ανώνυμες (χωρίς όνομα) συναρτήσεις και μπορείτε να τα εκχωρήσετε σε μια μεταβλητή, να τα περάσετε ως παραμέτρους ή να τα χρησιμοποιήσετε ως τιμή επιστροφής. Τα closures είναι ένα από τα πιο ισχυρά χαρακτηριστικά του Swift και χρησιμοποιούνται συχνά σε μεθόδους όπως map, filter, sorted του Array, σε completion handlers και σε ασύγχρονες λειτουργίες. Σε αυτό το άρθρο, θα εξετάσουμε τη βασική χρήση των closures, τη σύνταξή τους, τα trailing closures και τη συμπεριφορά capturing με λεπτομερή παραδείγματα.
Βασική Σύνταξη ClosureΈνα closure έχει την εξής δομή:
Απλό Παράδειγμα:
Χωρίς παραμέτρους και τύπο επιστροφής:
Πέρασμα Closure ως Παραμέτρου σε Συναρτήσεις
Τα closures χρησιμοποιούνται συχνότερα ως completion handlers.
func islemYap(sayi1: Int, sayi2: Int, operasyon: (Int, Int) -> Int) -> Int {
return operasyon(sayi1, sayi2)
}
// Χρήση
let toplam = islemYap(sayi1: 5, sayi2: 3) { (a, b) in return a + b }
print(toplam) // 8
// Πιο σύντομο
let carpim = islemYap(sayi1: 4, sayi2: 5) { $0 * $1 }
print(carpim) // 20Με shorthand syntax όπως $0, $1 μπορείτε να χρησιμοποιήσετε χωρίς να γράφετε ονόματα παραμέτρων.
Σύνταξη Trailing ClosureΑν η τελευταία παράμετρος μιας συνάρτησης είναι closure, μπορεί να γραφτεί έξω από τις παρενθέσεις.
// Κανονική χρήση
UIView.animate(withDuration: 1.0, animations: { view.alpha = 0 })
// Trailing closure (πιο καθαρό)
UIView.animate(withDuration: 1.0) { view.alpha = 0 }Χρήση Closure σε Συνήθεις Μεθόδους Arraylet sayilar = [3, 1, 4, 1, 5, 9, 2]
// map: Μετατροπή κάθε στοιχείου
let kareler = sayilar.map { $0 * $0 }
print(kareler) // [9, 1, 16, 1, 25, 81, 4]
// filter: Επιλογή αυτών που ταιριάζουν στην συνθήκη
let ciftler = sayilar.filter { $0 % 2 == 0 }
print(ciftler) // [4, 2]
// sorted: Ταξινόμηση
let sirali = sayilar.sorted { $0 < $1 }
print(sirali) // [1, 1, 2, 3, 4, 5, 9]Capturing Values (Σύλληψη Τιμών)Τα closures μπορούν να "συλλάβουν" (capture) τις μεταβλητές από το περιβάλλον τους. Αυτό είναι ένα χαρακτηριστικό που χρειάζεται προσοχή, ειδικά σε ασύγχρονες λειτουργίες.
func asenkronIslem(completion: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
completion()
}
}
var mesaj = "Başlangıç"
let closure = {
print(mesaj) // Το closure συλλαμβάνει τη μεταβλητή mesaj
}
mesaj = "Değiştirildi"
closure() // Έξοδος: DeğiştirildiΔιαχείριση weak/self με Capturing List (πρόληψη memory leak):class Ornek {
var deger = 0
lazy var closure: () -> Void = { [weak self] in
guard let self = self else { return }
print(self.deger)
}
}Τα @escaping closures πρέπει να δηλώνονται αν εκτελούνται μετά την έξοδο από τη συνάρτηση.
Σύγκριση: Closure vs Κανονική Συνάρτηση
- Χαρακτηριστικό: Όνομα Κανονική Συνάρτηση: Υπάρχει Closure: Συνήθως όχι (ανώνυμο)
- Χαρακτηριστικό: Ορισμός Κανονική Συνάρτηση: Με def Closure: Με {} μπλοκ
- Χαρακτηριστικό: Πέρασμα ως παραμέτρου Κανονική Συνάρτηση: Ναι Closure: Πολύ εύκολο και συχνά χρησιμοποιούμενο
- Χαρακτηριστικό: Trailing syntax Κανονική Συνάρτηση: Όχι Closure: Ναι (πιο αναγνώσιμο)
- Χαρακτηριστικό: Capturing Κανονική Συνάρτηση: Όχι Closure: Ναι (συλλαμβάνει τιμές από το περιβάλλον)
- Χαρακτηριστικό: Πεδίο Χρήσης Κανονική Συνάρτηση: Γενικού σκοπού Closure: Completion handler, map/filter κλπ.
Καλύτερες Πρακτικές• Σε σύντομα closures χρησιμοποιήστε shorthand $0, $1.
• Σε ασύγχρονα closures δηλώστε @escaping.
• Για πρόληψη memory leak χρησιμοποιήστε [weak self] ή [unowned self].
• Για αναγνωσιμότητα, βγάλτε πολύ μεγάλα closures σε ξεχωριστές συναρτήσεις.
ΣυμπέρασμαΤα closures στο Swift κάνουν τον κώδικα σας πιο ευέλικτο και σύντομο. Ειδικά σε υψηλού επιπέδου μεθόδους (map, filter, reduce) και ασύγχρονο προγραμματισμό είναι απαραίτητα. Ξεκινήστε με απλά παραδείγματα για να εξοικειωθείτε, με τον καιρό θα τα χρησιμοποιείτε άνετα σε completion handlers και animations.
Για πρακτική, γράψτε μια εφαρμογή επεξεργασίας δεδομένων σε ένα array χρησιμοποιώντας συνδυασμό map, filter και reduce!
Περιμένω τις ερωτήσεις σας στα σχόλια.