Delegate — је образац пројектовања при којем један објекат делегира извршење задатка другом објекту. У iOS-у образац се реализује кроз Swift протоколе и @protocol Objective-C. Delegation је један од фундаменталних образаца Cocoa Touch, који се користи у UITableViewDelegate, UITextFieldDelegate и стотинама других Apple API-ја. Према Apple Developer Documentation (2025), око 70% системских UIKit класа користи делегате за прилагођавање понашања без наслеђивања.
Главно
Delegate — је образац понашајног пројектовања који омогућава објекту да пренесе део својих обавеза другом објекту. За разлику од наслеђивања, где класа дете преписује методе родитеља, делегат користи композицију: објекат-власник чува референцу на делегата и позива његове методе у одређеним тачкама.
Делегат дефинише протокол — скуп метода које делегат може да имплементира. Методе се деле на обавезне (required) и опционе (optional). У Swift-у опционе методе протокола се обележавају кључном речи @objc optional.
Објекат А (власник) садржи својство delegate — weak-референцу на објекат Б (делегат). Када се догоди догађај, А проверава да ли Б имплементира одговарајући метод протокола и позива га. Weak-референца је обавезна: без ње делегат не може бити ослобођен из меморије, јер га власник држи јаком референцом.
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-референцу.
Имплементација делегата у Swift-у укључује три корака: декларисање протокола, креирање weak својства delegate у власнику и имплементација протокола у класи-делегату. Размотримо на примеру прилагођеног UITextField-а са валидацијом.
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 | NotificationCenter | Closure |
|---|---|---|---|
| Тип везе | 1:1 | 1:N | 1:1 |
| Повезаност | Слаба (кроз протокол) | Веома слаба (кључ-стринг) | Средња (хватање контекста) |
| Типска безбедност | Потпуна | Нема (Any?) | Потпуна |
| Ризик retain cycle | Не (weak) | Не | Да (хватање self) |
| Када применити | Комплексни callback-и са више метода | Догађаји који интересују многе | Једноставна затварања са 1-2 callback-а |
Delegate је оптималан када треба пренети низ повезаних догађаја једном примаоцу. NotificationCenter је бољи за broadcast обавештења. Closure — за једноставне асинхроне операције, као што је completion handler у URLSession.
Objective-C користи @protocol и @optional за декларисање делегата. За разлику од Swift-а, све методе протокола су подразумевано опционе. Кључна разлика — позив respondsToSelector: пре слања поруке делегату, јер метод можда није имплементиран.
@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.
Ако објекат-власник надживи делегата, а референца остане, позив метода делегата доводи до EXC_BAD_ACCESS. Weak-референца решава овај проблем аутоматски: након ослобађања делегата својство постаје nil. Међутим, у вишенитним сценаријима вреди додатно проверавати делегата у главној нити.
Протокол са 20+ метода крши принцип раздвајања интерфејса (ISP). UITableViewDelegate садржи око 30 опционих метода — ово је историјски изузетак. У властитим протоколима боље је поделити одговорност на неколико малих протокола, сваки са својом улогом.
Системски Apple API-ји активно користе образац Delegate. Размотримо три кључна примера из UIKit-а који се срећу у свакој iOS апликацији.
| API | Протокол | Кључне методе |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Сваки од ових протокола имплементира различите аспекте понашања: UITableViewDelegate управља изгледом и реакцијом на додире, UITextFieldDelegate контролише унос текста, URLSessionDelegate обрађује мрежне догађаје. Ово демонстрира флексибилност обрасца: делегат се може прилагодити било којој области одговорности.
Често постављана питања
Delegate управља понашањем и изгледом (висина ћелије, реакција на додир). DataSource пружа податке (број редова, садржај ћелија). У UITableViewDelegate и UITableViewDataSource — ово су два одвојена протокола, који деле одговорност за презентацију и податке.
Weak-референца спречава retain cycle. Власник (нпр. UITableView) држи референцу на делегата само као weak. Ако делегат (UIViewController) поседује табелу, јака референца на делегата створила би циклус: ViewController → UITableView → Delegate (ViewController). Weak прекида овај циклус.
У SwiftUI-ју образац Delegate се користи ређе — замењују га @Binding, @State и затварања. Ипак, delegate се и даље користи за интеграцију UIKit-а кроз UIViewRepresentable. На пример, MKMapViewDelegate и WKUIDelegate остају актуелни при облагању UIKit компонената у SwiftUI.
@objc optional омогућава декларисање опционих метода у Swift протоколу. То је механизам компатибилности са Objective-C runtime-ом. Без @objc све методе Swift протокола су подразумевано обавезне. Optional се користи у UIKit протоколима, где делегат може имплементирати само потребне методе.
Један објекат може имати само једног делегата за свако delegate својство. Ако је потребно обавестити више објеката, користите multicast delegate, низ делегата или NotificationCenter. Образац Delegate је првобитно дизајниран као однос 1:1.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође