So meistern Sie das Swift-Vorstellungsgespräch: Fortgeschrittenes Niveau
\n\nEin Vorstellungsgespräch für eine Position als Senior iOS-Entwickler oder Team Lead ist nicht nur eine Überprüfung der Syntaxkenntnisse. Interviewer möchten ein tiefes Verständnis der inneren Funktionsweise von Swift sehen, die Fähigkeit, Architektur zu entwerfen und Fallstricke zu vermeiden. In diesem Artikel analysieren wir 4 Blöcke mit fortgeschrittenen Fragen, die häufig in Vorstellungsgesprächen vorkommen. Jeder Block wird von Codebeispielen und Erklärungen begleitet, die Ihnen helfen, jede Frage souverän zu beantworten.
\n\n1. Automatic Reference Counting (ARC) und Speicherverwaltung
\n\nARC ist eines der Schlüsselthemen. Der Interviewer könnte nach starken Referenzzyklen (strong reference cycles) und Möglichkeiten zu deren Auflösung fragen.
Frage: Was ist eine Capture List und wie hilft sie, Retain Cycles in Closures zu vermeiden?
\n\nClosures in Swift sind Referenztypen. Wenn eine Closure self erfasst (z. B. eine Eigenschaft einer Klasse) und das Objekt selbst diese Closure speichert, entsteht ein Zyklus. Die Lösung ist die Verwendung einer Capture List mit den Modifikatoren [weak self] oder [unowned self].
class NetworkManager {\n var onComplete: (() -> Void)?\n\n func fetchData() {\n // GEFÄHRLICH: Erfassung einer starken Referenz\n onComplete = {\n self.processData() // retain cycle!\n }\n\n // RICHTIG: schwache Erfassung\n onComplete = { [weak self] in\n guard let self = self else { return }\n self.processData()\n }\n }\n\n func processData() { print("Processing...") }\n deinit { print("NetworkManager deallocated") }\n}\n\nWichtig: Verwenden Sie unowned self nur, wenn Sie sicher sind, dass das Objekt zum Zeitpunkt des Closure-Aufrufs noch existiert. Andernfalls verwenden Sie weak self.
2. Generics und Protokolle mit associatedtype
\n\nDas fortgeschrittene Niveau setzt die Fähigkeit voraus, generischen Code zu schreiben und die Einschränkungen von Protokollen mit assoziierten Typen zu verstehen.
\n\nFrage: Was ist der Unterschied zwischen einem Protokoll mit associatedtype und einem Protokoll mit Generics? Wie verwendet man Opaque Types (some)?
\n\nEin Protokoll mit associatedtype kann nicht als eigenständiger Typ verwendet werden. Zur Verbergung des konkreten Typs wird das Schlüsselwort some (opaque return type) verwendet, das in Swift 5.1 eingeführt wurde.
// Protokoll mit assoziiertem Typ\nprotocol Container {\n associatedtype Item\n mutating func append(_ item: Item)\n var count: Int { get }\n}\n\n// Implementierung\nstruct IntBox: Container {\n typealias Item = Int\n private var items: [Int] = []\n mutating func append(_ item: Int) { items.append(item) }\n var count: Int { items.count }\n}\n\n// Funktion gibt "some Container" zurück – der konkrete Typ ist verborgen\nfunc makeContainer() -> some Container {\n return IntBox()\n}\n\n// Verwendung\nvar box = makeContainer()\nbox.append(42)\nprint(box.count) // 1\n\nDieser Ansatz ermöglicht eine flexible Änderung der Implementierung, ohne den aufrufenden Code zu ändern. Dies wird oft im Zusammenhang mit SwiftUI und dem Builder-Muster abgefragt.
\n\n3. Multithreading: GCD und OperationQueue
\n\nKenntnisse von Grand Central Dispatch und Operationen sind ein Muss für jeden iOS-Entwickler. Fortgeschrittene Fragen betreffen Deadlocks, Thread-Sicherheit und Prioritäten.
\n\nFrage: Wie vermeidet man Data Races beim Arbeiten mit gemeinsamen Daten aus verschiedenen Threads?
\n\nEin Data Race tritt auf, wenn zwei Threads gleichzeitig ohne Synchronisation in denselben Speicherbereich lesen und schreiben. Die Lösung ist die Verwendung einer serialisierten Warteschlange oder einer Sperre.
\n\nclass ThreadSafeCounter {\n private var count = 0\n // Serialisierte Warteschlange zur Synchronisation\n private let queue = DispatchQueue(label: "com.example.counter")\n\n func increment() {\n queue.sync {\n count += 1\n }\n }\n\n func getCount() -> Int {\n return queue.sync