Det tager nogen tid at forstå memory-håndteringen til fulde, og selv
når man forstår det til fulde, kan man lave fejl. Se mine kommentarer
i koden nedenfor...
On 24-Dec-08, at 1:19 PM, kspr wrote:
> Hej,
>
> Jeg er helt ny til iPhone programmering og applikations programmering
> generelt og har derfor et spørgsmål vedr. et eksempel jeg følger fra
> bogen: The iPhone's Developers Cookbook.
>
> @interface HelloController : UIViewController
> {
> UIImageView * contentView;
Det er egentlig ikke nødvendigt at have en member-variabel til dette,
da du ligeså godt bare kan bruge den UIViewController-indbyggede
'view' variabel. Men det skader heller ikke det store, ud over at det
er med til at forvirre dig, tror jeg. De er nemlig det samme - d.v.s.
peger på samme adresse i hukommelsen.
> }
> @end
>
> @implementation HelloController
> ...
> - (void)loadView
> {
> //Load an application image and set it as the primary view
> contentView = [[UIImageView alloc] initWithFrame:[[UIScreen
> mainScreen] applicationFrame]];
Per konvention i Cocoa retainer alle kald, der begynder med "init",
den returnerede adresse. D.v.s. at adressen, som contentView peger på,
har et retainCount på 1 lige nu.
> [contentView setImage:[UIImage imageNamed:@"helloworld.png"]];
> self.view = contentView;
Her er en af eksemplerne på, hvorfor jeg aldrig bruger dot-notationen
i forbindelse med Objective-C objekter. Det er nemlig meget svært at
se, at ovenstående kald på kompileringstidspunktet bliver lavet om til
[self setView:contentView].
Og her ved den øvede Cocoa-programmør, at den metode først retainer
den givne adresse (som contentView peger på) og dernæst releaser den
adresse, som view-variablen tidligere pegede på og retainede.
retainCount er altså 2 på nuværende tidspunkt. Når den gamle view-
adresse releases, kommer dens retainCount ned på 0 og bliver frigivet
til ny brug.
Første gang koden køres, er view lig nil, så der sker ikke noget, når
den bliver releaset.
Efter min mening skaber det som sagt kun forvirring at køre parallelt
med contentView-variablen her. Det kunne ligeså godt bare have været
en lokal variabel. Og endelig; dot-syntaksen bruger jeg kun til
structs, så jeg let i min kode kan se hvad der er objekter, og hvad
der er, ja, structs. Og de opfører sig meget forskelligt i.f.m.
hukommelseshåndering.
> [contentView release]; //reduce retain count by one
Som kommentaren siger, reduceres retainCount med 1, så vi er nu nede
på 1. Husk på; self.view og contentView peger på den samme adresse, og
det er adressen der retaines og releases. Et retainCount på 1 er lige
nok til, at hukommelsen ikke overskrives med nyt indhold og sørger
samtidig for, at når det nye view sættes - f.eks. næste gang denne
metode kører - så kommer den gamle view-adresse ned på 0, og
hukommelsen frigves.
Husk også på, at -loadView kan kaldes flere gange for det samme view,
hvis f.eks. viewet bliver dækket af et andet, og hukommelsen bliver
knap. Så kan viewet blive releaset og hukommelsen frigivet, indtil
viewet skal vises igen, hvor -loadView så bliver kaldt igen.
Og husk også på (jo-jo, der er en masse at huske på), at -loadView kun
er aktuelt, hvis du opbygger dit viewhierarki programmatisk - altså du
ikke bruger Interface Builder.
> //Provide support for auto-rotation and resizing
> contentView.autoresizesSubviews = YES;
> contentView.autoresizingMask = (UIViewAutoresizingFlexibleWidth |
> UIViewAutoresizingFlexibleHeight);
> }
> ...
> - (void)dealloc
> {
> [contentView release];
> [super dealloc];
Her kan du få en runtime-fejl (BAD_ACCESS), da contentView lige er
blevet releaset (retainCount=0), og når super (UIViewController) så
releaser sin vew-variabel (samme adresse), så kommer du ned på -1.
Dette bliver kun et problem i runtime, og hvis der allokeres
hukommelse mellem de to release-kald. Det gør der sandsynligvis ikke,
men du kan ikke vide det, og du leger med ilden. Brugeren vil se, at
din app fejler, og vil være mere tilbøjelig til at slette den fra sin
iPhone.
For at undgå dette, anbefaler jeg, at du dropper contentView helt, da
den er unødvendig og gør din kode enklere og mindre fejlbarlig, eller
at du ikke releaser den i -loadView ovenfor.
> }
> @end
>
> Er der nogle der har forklare mig, så jeg forstår det. Hvorfor jeg i
> min -loadView metode først kan release min objekt variable contentView
> og så bagefter stadig sagtens kan sætte egenskaber for mit objekt? Har
> jeg ikke lige "drabt" det ved at release det fra hukommelsen og
> ydermere hvordan kan det være at jeg release'r det endnu en gang til i
> min -dealloc metode? Håber virkelig I kan give mig en ordenlig
> forklaring for jeg tror det er det jeg mangler før end at jeg forstår
> hukommelse allokering korrekt. Tak.
Håber det gjorde tingene lidt klarere...