Vad gör var
faktiskt gör, och varför ställer inte in myObject = null
faktiskt ta bort objektet? Dessa frågor knyter till ett grundläggande begrepp i kodning, relevant om ditt språk är AS3, JavaScript eller C #, och kan förstås med hjälp av några vanliga föremål från brevpapperskåpet.
Låt oss börja med grunderna. Antag att du vill lagra din väns ålder:
var ageOfBill: Number = 24;
(Jag ska använda AS3 för det här exemplet, men de grundläggande begreppen är desamma i JavaScript och C #.
I JavaScript är syntaxen nästan densamma, men vi anger inte att ålder är ett nummer:
var ageOfBill = 24;
I C # använder vi inte var
sökord, men vi anger typen av variabeln:
kort ålderOfBill = 24;
Inte tillräckligt för att vara förvirrande, hoppas jag.)
Så vad händer här? Tänk på det här sättet:
var
(eller kort
, i C #) betyder "få en ny post-it-anteckning".ageOfBill
betyder "skriv ageOfBill
överst i penna ".= 24
betyder "skriv 24
på anteckningen, i blyertspenna ".Men om vi senare inser att Bill faktiskt är yngre än vi trodde?
var ageOfBill = 24; // ... senare ... ageOfBill = 20;
Det betyder bara att vi hittar vår ageOfBill
notera, radera 24
, och skriv 20
på det istället.
Vi skulle kunna skriva var
igen:
var ageOfBill: Number = 24; // ... senare ... var ageOfBill: Number = 20;
... men det här är inte bra kod, för var
säger "få en ny post-it-anteckning". Om du gör det kommer kompilatorn vanligtvis att räkna ut vad du menar - det vill säga att du vill ändra vad som är skrivet på det existerande ageOfBill
Post-it not i stället för att faktiskt få en ny en - men det kommer troligen att klaga.
Varning: # 3596: Dubbla variabel definition.
Det beror på språket och på din kodande miljö.
Så kan vi be kompilatören att få en ny Post-it-anteckning och skriva en etikett på den i penna, utan att skriva något på det i blyertspenna? Kanske kan vi göra det för Bills vän Marty, vars ålder vi inte vet:
var ageOfMarty: Number;
Egentligen (i AS3, åtminstone) kommer detta att få en ny Post-it-anteckning, skriv ageOfMarty
överst, i penna ... och skriv sedan ett standardvärde på 0
där i blyertspenna:
Så, med andra ord, vi kan inte ha en Post-it-note så här utan att det tar något värde.
Okej, vad sägs om om vi vill lagra åldern till Bills bästa vän Ted, som vi vet är samma ålder?
var ageOfTed: Number = ageOfBill;
Vad som händer här är att datorn tittar på ageOfBill
Post-it, kopierar sedan numret skrivet på det i blyertspenna till en ny Post-it, som den skriver ageOfTed
över toppen i penna.
Detta är bara en kopia, dock; om vi då ändrar värdet på ageOfBill
det kommer inte att påverka ageOfTed
:
ageOfBill = 21;
Så! Det är allt ganska enkelt, och kanske till och med intuitivt. Låt oss nu prata om den första gemensamma smärtpunkten: arrays.
Tänk på en array som ett ringbindemedel.
(Jag skulle säga en rolodex ...
... men jag insåg att jag aldrig ens hade sett en i verkligheten.)
Varje ark inuti bindemedlet är som en av dessa Post-it-anteckningar, förutom utan den penna-skrivna etiketten överst. Istället hänvisar vi till varje ark med namnet på bindemedlet och sidnumret på arket.
Låt oss anta att vi har en uppsättning av alla våra vänner, inte i någon speciell ordning. Vem är på första sidan (sidan # 0)?
trace (vänner [0]);
(spår()
skriver bara raden till debug-utgången; i JavaScript, kan du använda console.log ()
och i C # kan du använda Console.WriteLine ()
för samma ändamål.)
Det är Bill!
Så, vad gör följande rad nu?
var första vän: String = vänner [0];
Det blir en ny Post-it-anteckning (på grund av var
nyckelord), skriver firstFriend
över toppen i penna, kopierar det som är skrivet på den första sidan av bindemedlet på den noten i penna.
(Kom ihåg, Sträng
betyder bara en bit text.)
Vi kan skriva över vad som skrivs på någon sida av bindemedlet, precis som med Post-it-anteckningarna:
vänner [0] = "Kyle";
... och naturligtvis påverkar detta inte firstFriend
Posta det.
Bindemedlet är en apt analogi, eftersom - precis som med en matris - du tar ta bort sidor, lägg till nya och omordna dem. Men kom ihåg att enskilda sidor fungerar precis som Post-it-noterna, förutom att de inte har egna pennetiketter, bara sidnummer.
Fortfarande ganska enkelt hoppas jag. Så här är en intressant fråga: vad händer när du gör följande?
var listOfNames: Array = friends;
Eh ...
Jag har lurat lite här, för jag pratade en massa om arrays utan att någonsin förklara hur vi skapar en i första hand. Så låt oss ta itu med det nu.
Antag att du skriver:
var vänner: Array = ["Bill", "Marty", "Ted"];
… Vad händer?
Tja, som vanligt, var vänner
betyder att vi får en ny Post-it-anteckning och skriver vänner
överst i penna:
Men vad skriver vi på det i blyertspenna?
Det är en knep fråga: vi skriver inte något.
Se, Array
betyder "få ett nytt ringbindemedel". Och ["Bill", "Marty", "Ted"]
betyder "sätt tre sidor i bindemedlet med dessa namn på det":
Och då? Det är enkelt! Vi håller fast vänner
Post-it-anteckningen till bindemedlets lock:
Nu när vi skriver:
trace (vänner [0]);
... vi vet att vi måste hitta Post-it-märkt vänner
, Kolla sedan på vad som är skrivet på den första sidan (sidan # 0) av bindemedlet som det sitter fast vid.
Det finns faktiskt väldigt få typer av variabler där ett värde skrivs på en Post-it-anteckning i penna. I AS3 är de enda sådana typerna (som kallas "primitives"):
siffra
Sträng
int
uint
Boolean
För allt annat - Objekt
, Filmklipp
, XML
, och så vidare - vi klibbar Post-it noten på själva objektet.
(Detaljerna är lite annorlunda i JavaScript och C #, men överallt gäller samma idé.)
Så låt oss återgå till vår tidigare fråga. När vi skriver:
var listOfNames: Array = friends;
… vad händer?
Det vet vi igen var listOfNames
betyder "få en ny post-it-anteckning och skriv listOfNames
över toppen i penna ". Och nu vet vi det Array
betyder att vi klibbar Post-it-noten till något (ett bindemedel), istället för att skriva något på Post-it-penna.
Tidigare, när vi har gjort något liknande, har vi kopierat innehållet i en Post-it-notering till en annan. Så här ska vi få ett nytt nytt bindemedel och kopiera alla sidor från vänner
binda in det?
Faktiskt nej! Allt vi gör är att hålla den här nya listOfNames
Post-it notera på samma bindemedel som vänner
Post-it lapp.
Nu, vänner
och listOfNames
var och en hänvisar till exakt samma matris. Så om vi skriver:
listOfNames [0] = "Emmett";
... då vänner [0]
kommer också vara Emmett
, därför att listOfNames [0]
och vänner [0]
hänvisa till exakt samma sida i exakt samma bindemedel! Och eftersom den sidan bara innehåller en sträng (som är en "primitiv" typ, kom ihåg), då har vi bara raderat det som tidigare skrivits på den sidan och skrivit Emmett
där istället.
null
Betyda?Sett så här, null
är ganska lätt att förstå. Detta påstående:
vänner = null;
... betyder bara "ta bort vänner
Post-it-anteckning från vad det än är för fast vid ".
De vänner
Post-it fortfarande existerar, Det är inte bara fast vid någonting. Så om du skriver:
trace (vänner [0]);
eller
vänner [0] = "Henry";
... då får du ett fel eftersom du försöker referera till den första sidan av bindemedlet som vänner
Post-det är fast vid - men det är inte fast vid någonting!
Så, för att vara tydlig, inställning vänner = null
påverkar inte bindemedlet alls. Du kan fortfarande få tillgång till det bara bra via listOfNames
. Och du kan även skriva:
vänner = listOfNames;
... att gå direkt tillbaka till den gamla situationen:
Som sagt, inställning vänner = null
påverkar inte bindemedlet direkt, men det kan ha en indirekt effekt.
Se, om det inte finns några Post-it-anteckningar fast vid bindemedlet alls, så finns det inget sätt för någon att komma åt bindemedlet någonsin igen. Det kommer bara att ligga, helt otillgängligt. Men med att alla dessa bindemedel (och andra föremål) ligger runt, helt övergivna, är ett verkligt slöseri med rymden - de klämmer upp datorns minne.
Det är där skräp samlare kommer in. Detta är ett verktyg som regelbundet kontrollerar några "förlorade" föremål och slänger dem i papperskorgen - och när de är borta är de borta för gott; om en matris är insamlad så är alla dess sidor också.
För de flesta praktiska ändamål påverkar detta dig inte alls. Objekt får bara skräp samlas om de är vilse och kan inte hittas av din kod. Om du har många av dem som ligger runt, kanske du märker en liten slump då och då när skräpkolven gör jobbet (det tar lite tid att aktivt samla soporna). Fördelen är att detta rensar upp mer utrymme (minne) för din app.
(Om du vill veta mer om detta ämne, läs Daniel Sidhions inlägg om skräppostsamling och objektpoolning.)
Okej, det finns ett enda större koncept att förstå - och det är det mest komplexa än.
Tänk på det här snippet:
var firstFriendDetails: Array = ["Bill", 20]; var secondFriendDetails: Array = ["Marty", 16]; var thirdFriendDetails: Array = ["Ted", 20]; var allavänner: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails];
Hur käften gör det den där arbete?
Låt oss börja med vad vi vet. De tre första linjerna är enkla; för var och en vi:
När det gäller den här raden:
var allavänner: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails];
... vi behöver lite sträng och lite tejp.
Vi kan tänka på den ena raden som likvärdig med detta kod:
var allavänner: Array = []; allFriends [0] = firstFriendDetails; allavänner [1] = secondFriendDetails; allfriends [2] = thirdFriendDetails;
Den första raden är lätt: få ett nytt bindemedel och en ny Post-it-anteckning, skriv alla vänner
på Post-it-noten och håll den till bindemedlet.
När det gäller den andra raden:
allFriends [0] = firstFriendDetails;
Kom ihåg att jag sa att varje sida i ett bindemedel är som en Post-it-anteckning, förutom utan att någonting skrivits i penna. Om den första sidan var en Post-it, så skulle vi helt enkelt hålla den på framsidan av firstFriendDetails
bindemedel, höger?
... men det kan inte vara både på framsidan av det bindemedlet och inuti det andra bindemedlet. Så, istället använder vi sträng:
Samma för de andra två:
Så när vi vill veta vad allFriends [2]
hänvisar till, vi öppnar bara alla vänner
binda till den sidan och följ strängen - som givetvis leder till thirdFriendDetails
bindemedel.
På samma sätt för allFriends [1] [0]
, vi först räknar ut vilket bindemedel allFriends [1]
hänvisar till, och sedan tittar vi på första sidan av den där bindemedel ... så allFriends [1] [0]
är Marty
!
Lägg nu all information tillsammans, och kom ihåg det när du läser det här koden:
var vänner: Array = ["Bill", "Marty", "Ted", "Emmett", "Henry"]; var currentIndex: int = 0; var nuvarande vän: sträng; medan (currentIndex < 5) currentFriend = friends[currentIndex]; trace(currentFriend); var lastFriend:String = currentFriend; trace(lastFriend);
Vad händer om vi ändrar värdet på currentFriend
inuti slingan?
var vänner: Array = ["Bill", "Marty", "Ted", "Emmett", "Henry"]; var currentIndex: int = 0; var nuvarande vän: sträng; medan (currentIndex < 5) currentFriend = friends[currentIndex]; currentFriend = "Herbert"; trace(currentFriend); trace(friends[0]);
Vad händer om arrayen innehåller icke-primitive objekt (MovieClips, bilder, arrayer, 3D-objekt, vad som helst)?
var vänner: Array = [första vän, andra vän, tredje vän, fjärde vän, femte vän]; var currentIndex: int = 0; varvarande vän: MovieClip; // eller ": Bild" eller ": Objekt" eller ": Array" eller vad som helst (currentIndex < 5) currentFriend = friends[currentIndex]; currentFriend = sixthFriend; //What is the value of friends[0] now?
Slutligen, vad om arrayen innehåller andra arrays, vilka själva innehåller primitiva?
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 vänner: Array = [firstFriendDetails, secondFriendDetails, thirdFriendDetails, fourthFriendDetails, fifthFriendDetails]; var currentIndex: int = 0; var nuvarande vän: Array; medan (currentIndex < 5) currentFriend = friends[currentIndex]; currentFriend[0] = "John"; currentFriend = ["Herbert", 29];
Vad tycker du värdet av vänner [3] [0]
kommer att vara efter det?
Här är några andra viktiga anteckningar du borde veta:
I AS3 och JavaScript är objekt som Arrays, förutom att varje sida hänvisas till av en etikett snarare än av dess sidnummer. Så du kan skriva:
var detaljerOfBill: Objekt = ; // "" betyder "skapa ett objekt" detailsOfBill.title = "Esquire"; detailsOfBill.bandName = "Wyld Stallyns"; detaljerOfBill.allNames = ["Bill", "S.", "Preston"];
… och det här är ungefär som att få ett nytt bindemedel, sticka a detailsOfBill
Post-it på framsidan och fylla den med tre sidor. Den första sidan har etiketten titel
skrivet över toppen i penna och ordet väpnare
skrivet på det med blyertspenna; Den andra sidan har etiketten bandnamnet
i penna och Wyld Stallyns
i blyertspenna. Den tredje sidan har etiketten allNames
men har inget skrivet på det i blyertspenna; I stället fäster en sträng till ett annat, vanligt bindemedel, vars sidor inte är märkta: den första sidan säger Räkningen
, den andra säger S.
, och den tredje säger Preston
, allt i blyertspenna.
(För att göra sakerna mer förvirrande är arrays tekniskt en speciell form av Objekt. Om du tycker att det är dåligt kan funktioner också ses som en typ av Objekt också! Men det är ett ämne för en framtida artikel ...)
Jag sa att föremål är skräp samlade om de inte har några Post-it-anteckningar fast vid dem, men det här är en förenkling. Om en sida av ett bindemedel pekar på ett objekt via en sträng (dvs om myArray [0] = myObject
eller liknande), så kommer det här objektet inte att vara skräp. Samma gäller för om en sida av ett bindemedel pekar på ett annat bindemedel (array), eller om sidan på ett objektbindemedel pekar på ett annat objektbindemedel ... och så vidare. Det gäller till och med om det enda sättet att komma åt objektet är genom en Post-it fast vid ett bindemedel som har en sida som är knuten till ett annat bindemedel, så många lager djupt som du vill gå.
I grund och botten samlar sopsamlaren bara ett objekt om det inte kan nås via någon annan variabel.
Detta förklarar varför objekt som finns på skärmen vanligen inte kan samlas in objekt. I AS3, om en MovieClip eller annan typ av DisplayObject finns i visningslistan, läggs den automatiskt till vad som i huvudsak är ett dolt utbud av visningsobjekt (som du kan komma åt via getChildAt ()). Liknande strukturer finns i JavaScript och C #. Så om en grafik finns på skärmen och du tar bort alla referenser till det från variabler, arrays och objekt, blir det fortfarande inte skräp samlat tills du tar bort det från visningslistan.
Jag hoppas det hjälper till att rensa upp saker. Det är verkligen ett förvirrande koncept när du först kommer över det: vissa variabler innehåller faktiskt en värde, medan andra bara innehåller en referens till ett objekt. Oroa dig inte om du inte är 100% säker på exakt vad som händer det kommer att ge mycket mer mening efter lite träning (och några misstag!).
Men om du har några specifika frågor om det, håll bara en kommentar nedan och jag gör mitt bästa för att svara.
Tack för att du läser!