Sådan registreres automatisk en hukommelseslækage i iOS

To af de største problemer, som iOS-udviklere står overfor, er lækager eller fastholdelsescyklusser. Begge kan bringe flere ulemper ved en app, såsom højt hukommelsesforbrug, tilfældige nedbrud, dårlig ydelse osv.

På grund af dette hos Wolox besluttede vi at undersøge og udvikle teknikker til at undgå disse problemer, gøre robust software og i sidste ende gøre udviklerens job lettere.

Hvad er en lækage?

En hukommelseslækage opstår, når en given hukommelsesplads ikke kan gendannes af systemet, fordi det ikke er i stand til at fortælle, om denne hukommelsesplads faktisk er i brug eller ikke.

Et af de mest almindelige problemer, der genererer hukommelseslækager i iOS, er fastholdelsescyklusser. Dette sker, når vi foretager cirkulære referencer mellem to eller flere objekter. En fastholdelsescyklus forhindrer, at hukommelsen, der bruges af disse objekter, frigives, selvom skaberen af ​​disse objekter frigiver dem.

For eksempel, hvis vi har en klasse person og en klasse lejlighed (som vist nedenfor)

Og hvis vi bruger det sådan:

Det genererer fastholdelsescyklusser, og ingen af ​​de to objekter frigives, fordi begge har en stærk henvisning til den anden, som det ses på nedenstående grafik.

Swift og Objekt-C har en referencetæller (ARC), der er ansvarlig for at frigive uanmeldt hukommelse (med andre ord den, der ikke bruges). Denne proces fungerer ved at tælle de stærke referencer, som hvert objekt har.

Stærke referencer øger referencetælleren med én, mens svage referencer ikke øger tælleren overhovedet (de indstiller værdien til nul, når objektets referenceantal når nul). Når en forekomst har nul referencer, frigives den. ARC registrerer dog ikke retningscyklusser.

I vores tilfælde har vi kun stærke og krydsede referencer, derfor vil begge tilfælde aldrig blive frigivet.

Sådan rettes lækager?

Hvad vi kan gøre er at erklære den ene reference som svag og den anden som stærk, derfor er den cirkulære reference brudt.

De to almindelige sager, hvor man finder denne type problemer, inkluderer:

  • Sag 1: Lukninger
  • Sag 2: Delegerede mønstre

En fastholdelsescyklus oprettet ved en lukning kan opstå, når en stærk henvisning til et objekt (lad os kalde det ObjektA) har en stærk henvisning til et andet objekt (lad os kalde det ObjektB) og ObjektB har en stærk henvisning til lukningen. I nedenstående grafik kan vi se en gengivelse af den beskrevne scene.

Tilfælde 1: Beholdningscyklusser forårsaget af lukninger kan løses ved hjælp af de 'ikke-ejede' eller 'svage' nøgleord på den måde den cirkulære reference brydes.

På den anden side kan tilbageholdelsescyklusser forårsaget af brugen af ​​delegerede mønster (sag 2) ske, når delegeret ikke erklæres for svag med en stærk henvisning til delegeret. Hvis vi f.eks. Har en View Controller, der implementerer delegeret fra en anden, som ikke er erklæret som svag, får vi en grafik som denne:

For mere information om hukommelsestyring, se dette link: http://krakendev.io/blog/weak-and-unowned-references-in-swift

Bindende analysator

Hos Wolox bruger vi et MVVM-mønster for at gøre slankere visningskontrollere mere ordnede, mindre og til at adskille ansvaret.

Bindingsanalysatoren var et af de første værktøjer, som vi brugte til at detektere lækager. Denne proces består i at vurdere forskellen mellem mængden af ​​indbinding og mængden af ​​indbindinger, der blev realiseret i synskontrollerne. Ved at binde mener vi at forbinde visningsmodellens egenskaber til controllerens visningssteder og handlinger. Med dette nummer kan vi derefter skelne mellem synskontrollere og visningsmodel fra dem, der ikke var.

Vi skal være forsigtige med ikke at opdage falske hukommelseslækager forårsaget af objekter med lang levetid (objekter, der forbliver i live under hele applikationens livscyklus), ligesom den visningsmodel, der administrerer fanebjælken. For at gøre det har vi et "registreringsdatabase", der holder styr på disse langvarige objekter. Dette register implementeres ved hjælp af en singleton-matrix, der indeholder identifikatorer for disse langvarige objekter.

Derudover er vi nødt til at registrere og fjerne hver gang vi udfører en binding eller afbinding af vores visningsmodeller. Til dette har vi også en singleton med en række strukturer, der indeholder dataene fra vores registrerede visningsmodeller. Desuden analyserer denne klasse, hvis vi fjerner en visningsmodel, der blev registreret som "lang levetid", som ikke burde frigives.

Efter at have lavet funktionen RegisterUnbinding og registerBinding, kan vi registrere bindings- eller bindevisningsmodellen. Denne metode er som følger:

Endelig skal vi tilføje en knap for at udløse dette værktøj.

Efter implementering af en ny funktion kan vi bruge den til at køre en BindingAnalyzer for at kontrollere, om der er lækager i appen.

Værktøjet Binding Analyzer er et, vi bruger hos Wolox, der giver fremragende resultater. Selv ved at bruge dette værktøj er der stadig en chance for, at der kommer en lækage, fordi hver gang der sker en ændring i kodebasen, skal vi køre værktøjet manuelt.

Detektering af automatisk hukommelse lækker med enhedstest

De problemer, der vekker grund af Binding Analyzer-værktøjet fik os til at tænke på, hvordan vi automatisk opdager lækager ved hjælp af enhedstest.

Når vi tilføjer en ny funktion, tester vi normalt dens opførsel, men tester vi, når der tilføjes nye lækager?

Hos Wolox bruger vi lækningstestning til funktioner som visningskontrollere og visningsmodeller. Brug af denne teknik eliminerer processen med manuel kontrol af lækager. En måde at implementere dette på er ved at bruge Nimble og Quick.

Som du kan se, at testene består af to dele, er den første del ansvarlig for at kontrollere, om lækagen findes i både synskontrollere og visningsmodeller.

  • I betragtning af en fabrik af synskontrollere oprettes en ny controller, der refererer til tidligere modeller.
  • Derfor skal det have en svag henvisning til visningskontrollen såvel som din visningsmodel.
  • Fjern nu den stærke reference, der er knyttet til visningskontrolleren, og skjul dens visning. Dette skal gøre det muligt at frigive visningskontrollen og visningsmodellen.
  • Endelig skal du kontrollere, om visningskontrolleren og visningsmodellen blev frigivet korrekt. Hvis påstanden mislykkes, blev der fundet en eller flere lækager i visningscontrolleren eller visningsmodellen.

Det anbefales at bruge en anden kø med en lille forsinkelse for at være sikker på, at visningskontrollerne og visningsmodellerne har den passende tid, der skal frigives.

Sådan implementeres fabrikkerne:

Den anden del af testen består i at registrere, om alle klasser strækker sig fra en UIViewController og sammenligne dem med mængden af ​​fabrikker, der er til stede. I tilfælde af at vi går glip af en eller flere, vil testen mislykkes (en konstant påmindelse om at teste den nye funktion). For at registrere alle klasser, der strækker sig fra en anden, kan vi gøre følgende:

Jo tidligere vi registrerer lækagerne, jo mere sandsynligt er det, at vi er i stand til at undgå og rette dem i kodebasen. Dette er især vigtigt, når projektet vokser, da det senere vil forhindre mange vanskeligheder.

Selvom dette værktøj stadig er i udviklingsstadiet, har det leveret enestående resultater. Vi håber at fortsætte med at forbedre og teste denne ramme sammen med andre projekter og teams. Med det ultimative mål at gøre det open source, så følg med!

Du er velkommen til at efterlade en kommentar, spørgsmål eller forslag nedenfor.