Referenzzyklus in Swift
Die Speicherverwaltung ist für einen Entwickler normalerweise nicht trivial. Dank der automatischen Referenzzählung (ARC) wird die Speicherverwaltung für iOS-Entwickler vereinfacht. Meistens funktioniert es einfach. Allerdings ist der Referenzzyklus sehr knifflig und ich habe viele erfahrene Entwickler gesehen, die solche Fehler gemacht haben.
In der offiziellen Dokumentation geht es hauptsächlich um den grundlegenden Referenzzyklus zwischen zwei Objekten A<->B. Was ich in diesem Artikel zeigen werde, ist der Referenzzyklus zwischen 3 oder 4 Objekten, der schwieriger zu erkennen ist.
Die App mit Memory Leak
Die Demo ist ein sehr einfaches iOS-Projekt mit zwei Bildschirmen. Im ersten Bildschirm „InitialViewController“ kann der Benutzer auf die Schaltfläche klicken, um den zweiten Bildschirm „SampleViewController“ zu starten. Anschließend kann der Benutzer auf die Schaltfläche „Schließen“ klicken, um den zweiten Bildschirm zu schließen. Der zweite Bildschirm ist mit der VIP-Clean-Architektur codiert.
Während der Benutzer die App gerne nutzt, geht Speicher verloren. Auf dem folgenden Bildschirm der Xcode-Funktion „Debug Memory Graph“ können wir sehen, dass jeweils fünf Instanzen von „SampleViewController“, „SamplePresenter“, „SampleInteractor“ und „SampleRouter“ durchgesickert sind. Wenn zu viel Speicher verloren geht, wird die App vom System beendet, was für den App-Entwickler sehr peinlich ist.
Referenzzyklus innerhalb mehrerer Objekte
Schauen wir uns den folgenden Quellcode genau an, um herauszufinden, warum der Speicher verloren geht.
class SampleViewController: UIViewController, SampleViewControllerInterface {
var interactor: SampleInteractorInterface?
// omitted the rest for simplicity
}
class SampleInteractor: SampleInteractorInterface {
var presenter: SamplePresenterInterface?
// omitted the rest for simplicity
}
class SamplePresenter: SamplePresenterInterface {
var viewController: SampleViewControllerInterface?
var router: SampleRouterInterface?
// omitted the rest for simplicity
}
class SampleRouter: SampleRouterInterface {
var viewController: UIViewController?
// omitted the rest for simplicity
}
Es gibt tatsächlich zwei Referenzzyklen darin:
- Interactor->Presenter->ViewController->Interactor
- Interactor->Presenter->Router ->ViewController->Interactor
Um das Problem zu lösen, können wir eine schwache Referenzierung wie die folgende Änderung verwenden.
Dann haben wir gemäß folgender Grafik keinen Referenzzyklus mehr für VIP.
Referenzzyklus im Zusammenhang mit der Blockerfassung
Hui! Das fühlt sich großartig an, denn wir haben gerade zwei Referenzzyklen festgelegt, sind aber noch nicht fertig. Wenn Sie die App jetzt ausführen, werden Sie feststellen, dass immer noch Speicherlecks vorliegen. Die Ursache ist der folgende Teil des Codes.
class CustomButton: UIView {
var buttonClickHander: (() -> Void)?
// omitted the rest for simplicity
}
class SampleViewController: UIViewController, SampleViewControllerInterface {
private lazy var customButton: CustomButton = {
let button = CustomButton()
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private func setUpUI() {
view.addSubview(customButton)
customButton.buttonClickHander = {
self.interactor?.customButtonClicked()
}
// ...
}
}
Sobald wir wissen, wo das Problem liegt, ist die Behebung einfach, siehe unten.
Zusammenfassung
Zusammenfassend haben wir zwei Arten von Referenzzyklen behandelt, die innerhalb mehrerer Objekte auftreten. Das Demoprojekt finden Sie hier . Die Korrektur wurde in einem Pull Request für Ihre einfache Überprüfung vorbereitet. Ich hoffe, dieser Artikel hilft Ihnen, den Referenzzyklus besser zu verstehen und ihn zu vermeiden.

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































