Forstå Variabler, Arrays, Loops og Null Post-it Note Analogy

Hva gjør Var faktisk gjør, og hvorfor setter ikke inn myObject = null faktisk fjerne objektet? Disse spørsmålene knytter seg til et grunnleggende konsept i koding, relevant om språkvalget ditt er AS3, JavaScript eller C #, og kan forstås ved å bruke noen få vanlige elementer fra brevpapirskapet.


Hva er en variabel?

La oss starte med det grunnleggende. Anta at du vil lagre vennens alder:

 var ageOfBill: Nummer = 24;

(Jeg skal bruke AS3 for dette eksemplet, men de grunnleggende konseptene er de samme i JavaScript og C #.

I JavaScript er syntaksen nesten det samme, men vi angir ikke at alder er et tall:

 var ageOfBill = 24;

I C # bruker vi ikke Var søkeord, men vi spesifiserer typen av variabelen:

 kort ageOfBill = 24;

Ikke annerledes nok til å være forvirrende, håper jeg.)

Så hva skjer her? Tenk på det på denne måten:

  • Var (eller kort, i C #) betyr "få en ny Post-it notat".
  • ageOfBill betyr "skrive ageOfBill over toppen, i penn ".
  • = 24 betyr "skrive 24 på notatet, i blyant ".

Hva om senere skjønner vi at Bill er faktisk yngre enn vi trodde?

 var ageOfBill = 24; // ... senere ... ageOfBill = 20;

Dette betyr bare at vi finner vår ageOfBill merk, slette 24, og skrive 20 på den i stedet.

Vi kunne skrive Var en gang til:

 var ageOfBill: Nummer = 24; // ... senere ... var ageOfBill: Nummer = 20;

... men dette er ikke bra kode, fordi Var sier, "få en ny post-it notat". Hvis du gjør dette, vil kompilatoren vanligvis finne ut hva du mener - det vil si at du vil endre hva som er skrevet på det eksisterende ageOfBill Post-it notat i stedet for faktisk å få en frisk en - men det vil trolig klage.

 Advarsel: # 3596: Dupliser variabeldefinisjon.

Det avhenger av språket og kodemiljøet ditt.

Så kan vi spørre kompilatoren om å få en frisk Post-it notat og skrive en etikett på den i penn, uten å skrive noe på det i blyant? Kanskje vi kunne gjøre dette for Bills venn Marty, hvis alder vi ikke vet:

 var ageOfMarty: Number;

Faktisk (i AS3, i det minste) vil dette få en ny Post-it notat, skriv ageOfMarty over toppen, i penn ... og skriv deretter en standard startverdi på 0 der i blyant:

Så, med andre ord, kan vi ikke ha en Post-it note som dette uten at det tar noen verdi.

Okay - hva med om vi vil lagre alderen til Bills beste venn Ted, som vi vet er den samme alderen?

 var ageOfTed: Number = ageOfBill;

Hva som skjer her er at datamaskinen ser på ageOfBill Post-it, kopierer deretter tallet skrevet på det i blyant til en ny Post-it, som den skriver ageOfTed over toppen i pennen.

Dette er bare en kopi, skjønt; hvis vi da endrer verdien av ageOfBill det vil ikke påvirke ageOfTed:

 ageOfBill = 21;

Så! Det er alt ganske greit, og kanskje til og med intuitivt. La oss nå snakke om det første vanlige smertepunktet: arrays.


Hva er en Array?

Tenk på en matrise som et ringbindemiddel.

(Jeg skulle si en rolodex ...

... men jeg skjønte at jeg aldri hadde sett en i virkeligheten.)

Hvert ark inne i bindemidlet er som en av de Post-it notatene, unntatt uten den penneskrevne etiketten over toppen. I stedet refererer vi til hvert ark ved navn på bindemidlet og sidens nummer på arket.

La oss anta at vi har en rekke av alle våre venner, i ingen bestemt rekkefølge. Hvem er på første side (side # 0)?

 spor (venner [0]);

(spor () skriver bare linjen til feilsøkingsutgangen; i JavaScript, kan du bruke console.log () og i C # du kan bruke Console.WriteLine () for samme formål.)

Det er Bill!

Så, hva gjør den neste linjen?

 var første venn: String = venner [0];

Det blir en ny Post-it notat (på grunn av Var søkeord), skriver firstFriend over toppen i penn, kopierer det som er skrevet på den første siden av bindemidlet på det notatet i blyant.

(Huske, string betyr bare et stykke tekst.)

Vi kan overskrive det som er skrevet på en hvilken som helst side av bindemidlet, akkurat som med Post-it-notatene:

 venner [0] = "Kyle";

... og selvfølgelig påvirker dette ikke firstFriend Post-it.

Bindemidlet er en apt analogi, fordi - akkurat som med en matrise - du tar ta ut sider, legg til nye, og omorganisere dem. Men husk, at enkelte sider fungerer som Post-it notater, bortsett fra at de ikke har egne pennetiketter, bare sidetall.

Fortsatt ganske grei, håper jeg. Så her er et interessant spørsmål: hva skjer når du gjør følgende?

 var listOfNames: Array = venner;

Uh ...


Du kan ikke skrive det på en Post-it

Jeg har lurt litt her, fordi jeg snakket en haug om arrays uten å forklare hvordan vi lager en i utgangspunktet. Så la oss takle det nå.

Anta at du skriver:

 var venner: Array = ["Bill", "Marty", "Ted"];

… Hva skjer?

Vel, som vanlig, var venner betyr at vi får en ny Post-it notat og skriv venner over toppen, i penn:

Men hva skriver vi på det i blyant?

Det er et triks spørsmål: vi skriver ikke hva som helst.

Se, Array betyr "få en ny ring bindemiddel". Og ["Bill", "Marty", "Ted"] betyr "sette tre sider i bindemidlet, med disse navnene på det":


Du kan ikke se sidene "Marty" og "Ted", men de er helt der.

Og så? Det er enkelt! Vi holder fast på venner Post-it notat til omslaget til bindemidlet:

Nå, når vi skriver:

 spor (venner [0]);

... vi vet at vi må finne Post-it-merket venner, så se på hva som er skrevet på den første siden (side # 0) av bindemidlet som den sitter fast på.

Det er faktisk svært få typer variabler hvor en verdi blir skrevet på en Post-it notat i blyant. I AS3 er de eneste slike typene (kalt "primitives"):

  • Nummer
  • string
  • int
  • UINT
  • boolean

For alt annet - Gjenstand, Filmklipp, XML, og så videre - vi holder Post-it notatet på selve gjenstanden.

(Detaljer er litt forskjellige i JavaScript og C #, men generelt gjelder samme idé.)

Så la oss komme tilbake til vårt tidligere spørsmål. Når vi skriver:

 var listOfNames: Array = venner;

… hva skjer?

Igjen vet vi det var listOfNames betyr "få en frisk Post-it notat og skriv listOfNames over toppen i penn ". Og nå vet vi det Array betyr at vi vil stikke Post-it notatet til noe (et bindemiddel), i stedet for å skrive noe på Post-it i blyant.

Tidligere, når vi har gjort noe lignende, har vi kopiert innholdet i en Post-it-notat til en annen. Så her skal vi få et nytt nytt bindemiddel og kopiere alle sidene fra venner bindemiddel inn i den?

Faktisk nei! Alt vi gjør er å holde denne nye listOfNames Post-it notat på samme bindemiddel som venner Post-it lapp.

Nå, venner og listOfNames hver refererer til nøyaktig samme matrise. Så hvis vi skriver:

 listOfNames [0] = "Emmett";

… deretter venner [0] vil også være Emmett, fordi listOfNames [0] og venner [0] referer til nøyaktig samme side i nøyaktig samme bindemiddel! Og fordi den siden bare inneholder en streng (som er en "primitiv" -type, husk), så har vi nettopp slettet hva som var skrevet på den siden tidligere og skrevet Emmett der i stedet.


Så hva gjør null Mener?

Sett slik ut, null er ganske lett å forstå. Dette utsagnet:

 venner = null;

... betyr bare, "fjern venner Post-it notat fra hva det for øyeblikket sitter fast på ".

De venner Post-it fortsatt finnes, det er bare ikke fast til noe. Så hvis du skriver:

 spor (venner [0]);

eller

 venner [0] = "Henry";

... da får du en feil, fordi du prøver å referere til den første siden av bindemiddelet som venner Post-det sitter fast - men det er ikke fast i noe!

Så, for å være klar, sette inn venner = null påvirker ikke bindemidlet i det hele tatt. Du kan fortsatt få tilgang til det bare fint via listOfNames. Og du kan til og med skrive:

 venner = listOfNames;

... å gå rett tilbake til den gamle situasjonen:


Søppelsamling

Som jeg sa, sette inn venner = null påvirker ikke bindemidlet direkte, men det kan ha en indirekte effekt.

Se, hvis det ikke er Post-it-notater som er fast i bindemidlet i det hele tatt, er det ingen måte for noen å få tilgang til bindematerialet noen gang igjen. Det vil bare ligge rundt, helt utilgjengelig. Men å ha alle disse bindemidlene (og andre gjenstander) som ligger rundt, helt oppgivet, er et stort sløsing med plass - de knytter opp dataminnet.

Det er der søppelmann kommer inn. Dette er et verktøy som regelmessig sjekker om noen "tapte" objekter, og kaster dem i søppel - og når de er borte, er de borte for godt; hvis en matrise er søppel samlet, så er alle sidene også.

For de fleste praktiske formål påvirker dette deg ikke i det hele tatt; gjenstander bare få søppel samlet hvis de er tapt og kan ikke bli funnet av koden din. Hvis du har mange av dem som ligger rundt, kan du merke et svakt lag hver nå og da, når søppelsamleren gjør jobben sin (det tar litt tid å aktivt samle søppelet). Fordelen er at dette fjerner mer plass (minne) for appen din.

(Hvis du vil vite mer om dette emnet, les Daniel Sidhions innlegg på søppelsamling og objektbassing.)


Arrays of Objects

Ok, det er et enda større konsept å forstå - og det er det mest kompliserte ennå.

Vurder denne koden:

 var firstFriendDetails: Array = ["Bill", 20]; var secondFriendDetails: Array = ["Marty", 16]; var thirdFriendDetails: Array = ["Ted", 20]; var allFriends: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails];

Hvordan pokker gjør at arbeid?

La oss starte med det vi vet. De tre første linjene er enkle; for hver og en vi:

  • Få et nytt bindemiddel.
  • Sett inn en side med vennens navn skrevet på det i blyant.
  • For nsert en annen side med vennens alder skrevet på den i blyant.
  • Få en frisk Post-it og skriv riktig etikett over toppen i pennen.
  • Fest Post-it til bindemidlet.

Når det gjelder denne linjen:

 var allFriends: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails];

... vi trenger litt streng og litt tape.

Vi kan tenke på den ene linjen som å være lik denne koden:

 var allFriends: Array = []; allFriends [0] = firstFriendDetails; allFriends [1] = secondFriendDetails; allFriends [2] = thirdFriendDetails;

Første linjen er enkelt: få et nytt bindemiddel og en ny Post-it notat, skriv alle venner På Post-it notatet, og hold det til bindemidlet.

Når det gjelder den andre linjen:

 allFriends [0] = firstFriendDetails;

Husk at jeg sa at hver side i et bindemiddel er som en Post-it notat, bortsett fra uten noe som er skrevet i penn. Hvis den første siden var en Post-it, så ville vi bare holde den til forsiden av firstFriendDetails bindemiddel, høyre?

... men det kan ikke være både på forsiden av det bindemiddelet og inne i det andre bindemidlet. Så, i stedet bruker vi streng:

Samme for de andre to:

Så når vi vil vite hva allFriends [2] refererer til, vi åpner bare alle venner bind til siden og følg strengen - som selvfølgelig fører til thirdFriendDetails binder.

Tilsvarende, for allFriends [1] [0], vi først finne ut hvilken bindemiddel allFriends [1] refererer til, og så ser vi på den første siden av at bindemiddel ... så allFriends [1] [0] er Marty!


Loops

Legg nå all den informasjonen sammen, og hold den i bakhodet når du leser denne koden:

 var venner: Array = ["Bill", "Marty", "Ted", "Emmett", "Henry"]; var currentIndex: int = 0; var nåværende venn: streng; mens (currentIndex < 5)  currentFriend = friends[currentIndex]; trace(currentFriend);  var lastFriend:String = currentFriend; trace(lastFriend);

Hva om vi endrer verdien av currentFriend inne i løkken?

 var venner: Array = ["Bill", "Marty", "Ted", "Emmett", "Henry"]; var currentIndex: int = 0; var nåværende venn: streng; mens (currentIndex < 5)  currentFriend = friends[currentIndex]; currentFriend = "Herbert"; trace(currentFriend);  trace(friends[0]);

Hva om arrayet inneholder ikke-primitive objekter (MovieClips, bilder, arrayer, 3D-objekter, uansett)?

 var venner: Array = [første venn, andre venn, tredje venn, fjerde venn, femte venn]; var currentIndex: int = 0; var nåværende venn: MovieClip; // eller ": Bilde" eller ": Objekt" eller ": Array" eller hva som helst (currentIndex < 5)  currentFriend = friends[currentIndex]; currentFriend = sixthFriend;  //What is the value of friends[0] now?

Til slutt, hva om arrayet inneholder andre arrays, som selv inneholder primitiver?

 var firstFriendDetails: Array = ["Bill", 20]; var secondFriendDetails: Array = ["Marty", 16]; var thirdFriendDetails: Array = ["Ted", 20]; var fourthFriendDetails: Array = ["Emmett", 50]; var femteFriendDetails: Array = ["Henry", 36]; var venner: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails, fourthFriendDetails, fifthFriendDetails]; var currentIndex: int = 0; var nåværende venn: Array; mens (currentIndex < 5)  currentFriend = friends[currentIndex]; currentFriend[0] = "John"; currentFriend = ["Herbert", 29]; 

Hva synes du verdien av venner [3] [0] vil være etter det?


Andre biter og stykker

Her er noen andre viktige notater du bør vite:

objekter

I AS3 og JavaScript, er objekter som Arrayer, bortsett fra at hver side er referert til av en etikett i stedet for ved sidenummer. Så du kan skrive:

 var detaljerOfBill: Objekt = ; // "" betyr "opprett et objekt" detailsOfBill.title = "Esquire"; detailsOfBill.bandName = "Wyld Stallyns"; detaljerOfBill.allNames = ["Bill", "S.", "Preston"];

… og dette er på en måte som å få et nytt bindemiddel, stikker a detailsOfBill Post-it på forsiden, og fyll den med tre sider. Den første siden har etiketten tittel skrevet over toppen i pennen og ordet Esquire skrevet på det i blyant; den andre siden har etiketten bandName i penn og Wyld Stallyns i blyant. Den tredje siden har etiketten allNames men har ingenting skrevet på det i blyant; I stedet legger en streng til en annen, vanlig bindemiddel, hvis sider ikke er merket: den første siden sier Regning, den andre sier S., og den tredje sier Preston, alt i blyant.

(For å gjøre ting enda mer forvirrende, er arrays teknisk en spesiell form for objekt. Og hvis du synes det er dårlig, kan funksjoner sees som en type objekt også! Men det er et emne for en fremtidig artikkel ...)

Mer om Garbage Collection

Jeg sa at gjenstander er søppel samlet hvis de ikke har noen Post-it-notater fast på dem, men dette er en forenkling. Hvis en side av et bindemiddel peker på et objekt via streng (dvs. hvis myArray [0] = myObject eller lignende), vil det objektet ikke bli søppel samlet. Det samme gjelder for hvis en side av et bindemiddel peker til et annet bindemiddel (array), eller hvis siden på et objektbinder peker til et annet objektbinder ... og så videre. Det gjelder selv om den eneste måten å få tilgang til objektet, er gjennom en Post-it fast på et bindemiddel som har en side som er bundet til et annet bindemiddel, så mange lag dypt som du vil gå.

I utgangspunktet samler søppelsamleren bare et element hvis det ikke kan nås gjennom en hvilken som helst annen variabel.

Dette forklarer hvorfor objekter som er på skjermen, vanligvis ikke kan samles inn i objektet. I AS3, hvis en MovieClip eller annen type DisplayObject er i displaylisten, blir den automatisk lagt til det som egentlig er et skjult utvalg av skjermobjekter (som du kan få tilgang til via getChildAt ()). Lignende strukturer finnes i JavaScript og C #. Så hvis en grafikk er på skjermen, og du fjerner alle referanser til det fra variabler, arrayer og objekter, vil det fortsatt ikke bli søppel innhentet før du fjerner det fra displaylisten.


Noen spørsmål?

Jeg håper dette bidrar til å rydde opp ting. Det er sikkert et forvirrende konsept når du først kommer over det: noen variabler inneholder faktisk en verdi, mens andre bare inneholder en henvisning til et objekt. Ikke bekymre deg hvis du ikke er 100% sikker på hva som skjer; det vil gjøre mye mer fornuftig etter litt trening (og noen feil!).

Men hvis du har noen spesifikke spørsmål om det, bare hold en kommentar nedenfor, og jeg vil gjøre mitt beste for å svare.

Takk for at du leste!