Delegate — är ett designmönster där ett objekt delegerar utförandet av en uppgift till ett annat objekt. I iOS implementeras mönstret via Swift-protokoll och @protocol Objective-C. Delegation är ett av de fundamentala mönstren i Cocoa Touch, som används i UITableViewDelegate, UITextFieldDelegate och hundratals andra Apple API:er. Enligt Apple Developer Documentation (2025) använder cirka 70% av systemklasserna i UIKit delegater för att anpassa beteende utan arv.
Huvudpunkter
Delegate — är ett beteendedesignmönster som tillåter ett objekt att överföra en del av sina ansvarsområden till ett annat objekt. Till skillnad från arv, där underklassen åsidosätter förälderns metoder, använder delegaten komposition: ägarobjektet lagrar en referens till delegaten och anropar dess metoder vid specifika punkter.
Delegaten definierar ett protokoll — en uppsättning metoder som delegaten kan implementera. Metoderna delas in i obligatoriska (required) och valfria (optional). I Swift markeras protokollets valfria metoder med nyckelordet @objc optional.
Objekt A (ägaren) innehåller egenskapen delegate — en weak-referens till objekt B (delegaten). När en händelse inträffar kontrollerar A om B implementerar motsvarande protokollmetod och anropar den. Weak-referens är obligatorisk: utan den kan delegaten inte frigöras från minnet, eftersom ägaren håller den med en stark referens.
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)
// asynkron laddning
}
}Klassen DataLoader definierar protokollet LoaderDelegate och anropar delegatens metoder vid nyckelpunkter i laddningslivscykeln. AnyObject garanterar att protokollet endast kan implementeras av klasser — detta är nödvändigt för weak-referensen.
Implementering av delegaten i Swift omfattar tre steg: deklarera protokollet, skapa weak delegate-egenskapen i ägaren och implementera protokollet i delegatklassen. Låt oss titta på exemplet med en anpassad UITextField med validering.
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("Validering misslyckades: inmatningen är för kort")
}
}LoginViewController implementerar protokollet ValidatorDelegate och sätter sig själv som delegat för textField. Vid varje textändring anropar ValidatedTextField validate(_:), och om valideringen misslyckas — validatorDidFail(_:). Controller fungerar som medlare mellan View och valideringslogiken.
Valet mellan delegat, notifikationer och closures beror på antalet mottagare och graden av koppling mellan komponenter. Varje mekanism löser problemet med kommunikation mellan objekt, men med olika avvägningar.
| Egenskap | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Typ av anslutning | 1:1 | 1:N | 1:1 |
| Koppling | Svag (via protokoll) | Mycket svag (strängnyckel) | Medel (fånga kontext) |
| Typsäkerhet | Fullständig | Saknas (Any?) | Fullständig |
| Retain cycle risk | Nej (weak) | Nej | Ja (fånga self) |
| När ska användas | Komplexa callbacks med flera metoder | Händelser som intresserar många | Enkla closures med 1-2 callbacks |
Delegate är optimal när en serie relaterade händelser ska skickas till en mottagare. NotificationCenter är bättre för broadcast-notifikationer. Closure — för enkla asynkrona operationer, som completion handler i URLSession.
Objective-C använder @protocol och @optional för att deklarera delegater. Till skillnad från Swift är alla protokollets metoder som standard valfria. Den viktigaste skillnaden — anrop av respondsToSelector: innan meddelandet skickas till delegaten, eftersom metoden kanske inte är implementerad.
@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];
}
// asynkron bildladdning
}
@endDen viktigaste skillnaden i Objective-C: före anrop av en valfri metod krävs kontroll av respondsToSelector:. I Swift eliminerar protokollets valfria metoder denna kontroll — optional chaining (?.) hanterar automatiskt fallet med saknad implementering.
Fel vid användning av delegater leder till minnesläckor, appkrascher och ouppenbara buggar. Låt oss titta på fem av de vanligaste problemen.
Retain cycle — det vanligaste felet. Om delegate-egenskapen deklareras som strong, och delegaten i sin tur äger ägarobjektet, uppstår en retentionscykel. Båda objekten frigörs aldrig från minnet. Lösning: deklarera alltid delegaten som weak var i Swift eller @property (weak) i Objective-C.
Om ägarobjektet överlever delegaten och referensen finns kvar, leder anrop av delegatens metod till EXC_BAD_ACCESS. Weak-referens löser detta problem automatiskt: efter att delegaten frigörs blir egenskapen nil. I flertrådade scenarier är det dock värt att kontrollera delegaten även i huvudtråden.
Ett protokoll med 20+ metoder bryter mot principen om gränssnittsseparation (ISP). UITableViewDelegate innehåller cirka 30 valfria metoder — detta är ett historiskt undantag. I egna protokoll är det bättre att dela upp ansvaret i flera små protokoll, var och en med sin egen roll.
Apples system-API:er använder aktivt Delegate-mönstret. Låt oss titta på tre viktiga exempel från UIKit som förekommer i varje iOS-applikation.
| API | Protokoll | Nyckelmetoder |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Vart och ett av dessa protokoll implementerar olika aspekter av beteende: UITableViewDelegate hanterar utseende och reaktion på beröring, UITextFieldDelegate kontrollerar textinmatning, URLSessionDelegate bearbetar nätverkshändelser. Detta visar mönstrets flexibilitet: delegaten kan anpassas till vilket ansvarsområde som helst.
Vanliga frågor
Delegate hanterar beteende och utseende (cellhöjd, reaktion på beröring). DataSource tillhandahåller data (antal rader, cellinnehåll). UITableViewDelegate och UITableViewDataSource — det är två separata protokoll som delar ansvaret för presentation och data.
Weak-referens förhindrar retain cycle. Ägaren (t.ex. UITableView) håller referensen till delegaten endast som weak. Om delegaten (UIViewController) äger tabellen skulle en stark referens till delegaten skapa en cykel: ViewController → UITableView → Delegate (ViewController). Weak bryter denna cykel.
I SwiftUI används Delegate-mönstret mer sällan — det ersätts av @Binding, @State och closures. Dock används delegate fortfarande för UIKit-integration via UIViewRepresentable. Till exempel förblir MKMapViewDelegate och WKUIDelegate relevanta när UIKit-komponenter lindas in i SwiftUI.
@objc optional gör det möjligt att deklarera valfria metoder i ett Swift-protokoll. Detta är en kompatibilitetsmekanism med Objective-C runtime. Utan @objc är alla metoder i ett Swift-protokoll som standard obligatoriska. Optional används i UIKit-protokoll, där delegaten endast kan implementera de metoder den behöver.
Ett objekt kan ha endast en delegat för varje delegate-egenskap. Om flera objekt behöver meddelas, använd multicast delegate, en array av delegater eller NotificationCenter. Delegate-mönstret är ursprungligen utformat som en 1:1-relation.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också