Delegate: vad är det, delegationsmönster och hur det fungerar på iOS

Författare: IT Sectr Publicerad: 2026-03-17 Lästid: 10 min

Delegate — är ett designmönster där ett objekt delegerar utförandet av uppgifter till ett annat objekt via ett protokoll med fördefinierade metoder. Inom iOS-utveckling är Delegate ett av de grundläggande mönstren i Cocoa Touch, som används för asynkron meddelande utan direkt koppling mellan avsändare och mottagare. Enligt Apple Documentation (2025) tillämpas delegering i Foundation och UIKit för bearbetning av tabellhändelser, nätverksförfrågningar och platshantering. Mönstret säkerställer lös koppling av komponenter och återanvändning av kod.

Huvudpunkter

  • Delegate — mönster där objektet anförtror bearbetning av händelser till ett annat objekt via ett protokoll.
  • Protocol i Swift definierar uppsättningen metoder som delegate måste eller kan implementera.
  • Weak reference är obligatorisk för egenskapen delegate för att undvika retain cycle.
  • @objc optional gör det möjligt att göra protokollmetoder valfria att implementera.
  • URLSessionDelegate — asynkron delegate för bearbetning av nätverksförfrågningshändelser.

Vad är Delegate?

Delegate (delegat) — är ett objekt som implementerar ett specifikt protokoll och tar emot meddelanden om händelser från ett annat objekt. Delegation-mönstret är ett alternativ till arv: istället för att skapa en underklass för att åsidosätta metoder, delegerar objektet bearbetning av händelser till ett externt objekt. I iOS implementeras delegation via Swift-protokoll med obligatoriska och valfria metoder. Egenskapen delegate deklareras alltid som weak var för att undvika cykliska referenser mellan objekt.

Definiera Delegate-protokollet

Delegate-protokollet definierar interaktionskontraktet mellan objekt. Obligatoriska metoder måste implementeras av delegaten, annars kompileras inte koden. Valfria metoder markeras med attributet @objc optional och tillåter delegaten att reagera endast på nödvändiga händelser. Metodnamn följer konventionen: första parametern — avsändarobjektet, andra — händelsedata. Till exempel indikerar tableView(_:didSelectRowAt:) att avsändaren är UITableView och datan är index för den valda raden.

swift
// Delegate-protokoll
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)
}

// Klass som använder 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()
    }
}

Svag referens till delegate

Egenskapen delegate måste deklareras som weak var (svag pekare) för att förhindra retain cycle. Om referensen var stark skulle delegaten och det delegerande objektet hålla fast varandra och ARC skulle inte kunna frigöra deras minne. Delegate-protokoll ärver AnyObject (endast för klasser), vilket möjliggör användning av weak. Strukturer och enum kan inte vara delegater på grund av värdessemantik. Alternativ för value types — callback closures.

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

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — ingen retain cycle
        manager.startDownload(from: url)
    }

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

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

Hur fungerar Delegate-mönstret i iOS?

Delegate-mönstret fungerar enligt principen ”en-till-en”: ett avsändarobjekt kan bara ha en delegat åt gången. När en händelse inträffar kontrollerar avsändaren om delegate är inställd och anropar motsvarande protokollmetod. Fördel jämfört med direkt anrop — avsändaren känner inte till delegatens typ, bara att den överensstämmer med protokollet. Detta följer principen om beroendeinversion (DIP) från SOLID.

Delegatens livscykel

Delegaten sätts via tilldelning: someObject.delegate = self. Vid frigöring av delegaten blir egenskapen automatiskt nil på grund av weak-semantik. Innan delegate-metoden anropas kontrolleras det via optional chaining: delegate?.method(). Om delegate är nil — ignoreras anropet utan krasch. För valfria protokollmetoder används en extra kontroll: delegate?.responds(to: #selector(...)), även om denna kontroll i Swift vanligtvis är implicit genom deklaration av valfri metod.

Asynkron meddelande via Delegate

I en flertrådig miljö används delegate för asynkron återkoppling av resultat. URLSession tillhandahåller URLSessionDelegate med metoder som anropas vid mottagning av data, timeout eller autentiseringsfel. Delegate-metoder körs i URLSessions bakgrundskö, så dirigering till main queue krävs för UI-uppdateringar. Asynkron delegate blockerar inte den anropande tråden, vilket möjliggör fortsättning av andra uppgifter.

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: jämförelse av tillvägagångssätt

Delegate och Callback löser samma uppgift — asynkron meddelande — men på olika sätt. Delegate använder ett protokoll med namngivna metoder, callback — en closure med kontextfångst. Valet beror på antalet händelser, komplexiteten hos signaturer och arkitektoniska preferenser. Apple rekommenderar delegate för API:er med flera händelser (UITableView — 20+ metoder) och callback för engångsslutföranden.

När Delegate vinner

Delegate är att föredra när flera olika händelser från en källa behöver bearbetas. Till exempel meddelar CLLocationManager delegaten om platsförändring, behörighetsfel, in-/utträde ur geografiska zoner och förändring av tjänstestatus. Varje händelse är en separat protokollmetod med tydligt namn och typade parametrar. Delegate är också bekväm för konfiguration av beteende (should, will, did-metoder).

När Callback vinner

Callback är enklare för engångsförfrågningar med ett resultat. Completion handler i URLSession.dataTask upptar en rad vid anropspunkten jämfört med minst tre protokollmetoder. Callback är också mer naturlig för funktionella kedjor (map, flatMap, async/await). Vid nästling av mer än 2-3 nivåer blir dock callback Callback Hell, medan delegate alltid förblir platt.

Inbyggda Delegater i iOS SDK

iOS SDK innehåller dussintals inbyggda delegate-protokoll för olika delsystem. Var och en är utformad för ett specifikt interaktionsscenario. Enligt Apple Documentation (2025) är de mest använda delegaterna UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate och UNUserNotificationCenterDelegate. Dessa protokoll innehåller 3 till 30 metoder med varierande grad av obligatoriskhet.

UITableViewDelegate

UITableViewDelegate hanterar utseendet och beteendet hos tabellceller. Innehåller metoder för bearbetning av radval, inställning av cellhöjd, anpassade header/footer-vyer och svepaktioner. Alla protokollmetoder är valfria, vilket möjliggör implementering av endast nödvändig funktionalitet. I avsaknad av delegat fungerar tabellen med standardinställningar. Historiskt kombinerades delegate med UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate ger detaljerad kontroll över HTTP-förfrågningar. Delegatens metoder anropas vid mottagning av serversvar, data, slutförande av nedladdning. Specialiserade underprotokoll URLSessionTaskDelegate och URLSessionDataDelegate utökar grundfunktionaliteten för specifika uppgiftstyper. Delegate krävs för stöd av bakgrundsnedladdningar, SSL-certifikat och anpassad bearbetning av omdirigeringar.

DelegateMetoderÄndamål
UITableViewDelegate25Utseende och interaktion med tabell
UITextFieldDelegate8Bearbetning av textinmatning och tangentbord
CLLocationManagerDelegate12Platsuppdatering och geografiska zoner
URLSessionDelegate6Hantering av HTTP-session och certifikat
UNUserNotificationCenterDelegate4Bearbetning av push-meddelanden i förgrunden

Minneshantering vid arbete med Delegate

Minneshantering är en kritisk aspekt av att arbeta med delegate i iOS. ARC (Automatic Reference Counting) hanterar minnet automatiskt, men endast med korrekt användning av weak/unowned-referenser. Brott mot reglerna leder till minnesläckor eller förtida frigöring. Delegate deklarerad som strong skapar en retain cycle om ägaren av delegaten också håller en referens till det delegerande objektet.

Retain cycle via Delegate

Retain cycle uppstår när objekt A (ägaren) sätter sig själv som delegat för objekt B, och B håller en stark referens till delegate. Exempel: ViewController skapar URLSession, sätter self som delegat för sessionen, men URLSession håller som standard en stark referens till delegate om ingen delegateQueue anges. Lösning — kontrollera alltid API-dokumentationen för typen av referens till delegate (weak eller strong) och nollställ delegate explicit i deinit.

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

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

    deinit {
        // Nollställning av delegate i deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession med weak delegate via 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
        )
    }
}

Säker kontroll av delegate före anrop

Innan delegatens metod anropas måste man kontrollera att delegaten finns (inte nil) och implementerar den anropade metoden. För obligatoriska protokollmetoder krävs ingen kontroll — kompilatorn garanterar implementering. För valfria metoder använd respond(to:) eller optional chaining. Om delegaten har frigjorts blir weak-referensen automatiskt nil och delegatanropet ignoreras. Detta är säkert beteende som inte kräver ytterligare bearbetning.

Typiska fel vid implementering av Delegate

Utvecklare gör ofta misstag när de arbetar med Delegate-mönstret, särskilt i början av att lära sig iOS. Vanligast: retain cycle på grund av strong delegate, glömma att anropa delegate?.method(), felaktig signatur på protokollmetoder, inställning av delegate efter start av operation och flertrådiga kollisioner. Låt oss titta på varje misstag och sätt att förebygga dem.

Strong reference istället för weak

Det mest kritiska misstaget — att deklarera delegate-egenskapen som strong var istället för weak var. Detta skapar en retain cycle där varken delegaten eller det delegerande objektet kan frigöras. Konsekvenser: minnesläcka, applikationsfördröjning och dolda buggar. Lösning: använd alltid weak var för delegate och låt protokollet ärva AnyObject för att utesluta användning av value types som delegat.

Inställning av delegate efter operationens start

Om delegate sätts efter anrop av en asynkron metod kan de första händelserna gå förlorade. Exempel: anrop av startDownload() före tilldelning av manager.delegate = self leder till förlust av slutförandecallback om nedladdningen utförs synkront eller mycket snabbt. Lösning: sätt delegate före anrop av den asynkrona metoden och dokumentera initialiseringsordningen i protokollkommentarerna.

Vanliga frågor

Varför deklareras delegate som weak?

Weak förhindrar retain cycle mellan delegaten och det delegerande objektet. Om referensen var stark skulle objekten hålla fast varandra och ARC skulle inte kunna frigöra dem. Weak-referens blir automatiskt nil vid frigöring av delegaten. Detta är standardpraxis i Cocoa Touch sedan Objective-Cs uppkomst och har bevarats i Swift för bakåtkompatibilitet.

Vad är skillnaden mellan delegate och dataSource?

Delegate bearbetar händelser och hanterar beteende (cellhöjd, reaktion på klick). DataSource tillhandahåller data för visning (antal rader, celler). Delegate svarar på frågan “hur?”, dataSource — på frågan “vad?”. I iOS implementeras båda via protokoll, ofta i samma kontroller, men konceptuellt separerade.

Kan struct användas som delegate?

Nej, om protokollet ärver AnyObject (klassprotokoll). Weak-referenser är endast tillgängliga för reference types (klasser). För value types (struct, enum) använd callback closures eller en separat wrapper-klass. Om du kontrollerar protokollet kan du låta bli att ärva AnyObject, men då är weak förbjudet — välj medvetet mellan weak-delegate och struct-delegat.

Vad är metoden responds(to:) och vad används den till?

responds(to:) — är en metod i NSObjectProtocol som kontrollerar om objektet implementerar den angivna selektorn. Används för att kontrollera valfria @objc-protokollmetoder före anrop. Utan denna kontroll leder anrop av en oimplementerad valfri metod till NSInvalidArgumentException. I Swift för protokoll med @objc optional kan kontrollen vara implicit via optional binding.

Är Delegate en singleton?

Nej, delegate är ett delegationsmönster, inte en singleton. Till skillnad från en singleton kan delegaten bytas ut under körning och finns i en instans för varje delegerande objekt. Ett objekt kan vara delegat för flera avsändare. Singleton är ett skapandemönster som garanterar en unik instans av en klass och har inget att göra med delegering.

Sammanfattning

  • Delegate — mönster där objektet delegerar bearbetning av händelser till ett annat objekt via ett protokoll med typade metoder.
  • Weak var är obligatorisk för delegate-egenskapen för att förhindra retain cycle och minnesläckor.
  • Protokoll definierar obligatoriska och valfria (@objc optional) delegeringsmetoder.
  • iOS SDK innehåller 15+ inbyggda delegate-protokoll: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate är att föredra framför callback vid 3+ olika händelser från en källa (CLLocationManager).
  • Asynkron delegate används i URLSession för att hämta data och framsteg utan att blockera tråden.
  • Sätt delegate innan asynkron operation startar och nollställ den i deinit för säker minneshantering.

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.

Diskutera projektet

Läs också