En av de vanligaste frågorna jag ser på forum och få från kollegor är hur man debugger Error 1009, även känd som "Null Object Reference Error." Eller, som jag kallar det, "irriterande myggfelet från helvetet." Det väcker mycket, och tyvärr innehåller inte felet mycket information om källan till felet. I det här snabba tipset tar vi en titt på några steg du kan ta för att spåra denna mygga och squash det bra.
Denna del är den första uppföljningen till den mer generella? Fixing Bugs i AS3? handledning. Om du vill bättre förstå några av teknikerna i detta tips kanske du vill läsa det fullständigt först.
Det är synd att Adobe inte (eller kan inte) ge mer information om roten till det här felet. Först och främst är det ganska obtrusivt formulerat (ungefär som alla sina fel, men det här är mer än de flesta):
TypeError: Error # 1009: Kan inte komma åt en egenskap eller metod för en null objektreferens
Låt oss försöka sätta detta i vardagliga termer. Fel 1009 innebär att du har försökt att göra något med en variabel som du antar har ett värde, men det gör det inte. Flash gillar inte det. Du skulle inte gilla det heller; Tänk dig att du hade ett glas som du antog var fullt av den läckra drycken du själv valde, men var verkligen tom. Du tar tag i glaset och förväntar dig en uppfriskande sug, men du känner dig i stället av den tappande vikten av ett tomt glas. Det är ditt eget personliga fel 1009.
I ActionScript, om du gör det här:
Var s: String; trace (s.toUpperCase ());
Flash kommer bum hårt (en teknisk term för? Skapa ett fel?) När du kör koden. Variabeln s
kan ha förklarats, men dess värde är null
(vi satte aldrig värdet, precis deklarerade variabeln), så ringde toUpperCase
Metoden på den är besvärlig.
För att vara tydlig, för s
förklaras som en Sträng
, de kompilator tar ingen fråga med koden: det finns en variabel som heter s
, det är en Sträng
, och toUpperCase
är en giltig metod att ringa på Sträng
s. Felet vi får är a körning fel, vilket innebär att vi bara får det när vi kör SWF. Först när logiken utförs får vi nu se hur det visar sig.
Precis som med eventuella kör-tid fel, ibland är det ganska lätt att berätta vad som händer utan extra information. Men andra gånger är det bra att minska detta ytterligare. Vid denna tid försöker du aktivera? Tillåt debugging.? När detta är aktiverat får du fel som också ger dig radnummer. Alternativt kan du kanske? Debug Movie? genom att trycka på Command-Shift-Return / Control-Shift-Enter.
För att göra det, se den allmänna felsökningstipsartikeln,? Fasta fel i AS3?
Ibland är det tillräckligt. Att veta den specifika raden kan vara all information du behöver. Om inte, graver vi lite djupare i nästa steg.
Vår kära redaktör, Michael James Williams, inkapslade punkten i det här steget i en limerick, som jag gärna presenterar för dig nu med hans vänliga tillstånd:
AS3 fel en-oh-oh-nio
Är aldrig ett mycket bra tecken.
Inget behov av oro,
Hit Ctrl-Shift-Return
Och det kommer att ange orsaken (väl, linjen).
Om du har hittat den oupphörliga raden men fortfarande är osäker på vad som händer, plocka ut raden. Ta varje variabel i den linjen och spåra dem ut före felet.
Eftersom felet kommer när man får tillgång till en egenskap eller ringer en metod på en null-variabel, för att täcka dina baser bör du spåra några variabler och egenskaper som omedelbart följs av en punkt. Ta till exempel den här koden:
myArray.push (someSprite.stage.align.toLowerCase ());
Visst är det en ganska konstruerad kod som jag inte kan tänka mig en praktisk användning för, men du borde identifiera fyra totalt möjligt null
värden som nås med pricken:
myArray
: vi ringer till tryck
metod för denna variabelsomeSprite
: Vi har tillgång till skede
fast egendomskede
: Vi har tillgång till justera
fast egendomjustera
: vi ringer till toLowerCase
metodSå din debugging-kod kan se ut så här:
spår ("myArray:", myArray); spår ("someSprite:", someSprite); spår ("someSprite.stage:", someSprite.stage); spår ("someSprite.stage.align:", someSprite.stage.align);
Order är viktigt; om someSprite
är null objektet, men du testar för someSprite.stage.align
före testning för someSprite
, du hamnar med mindre definitiva resultat.
Nu är det också sunt förnuft som spelar in i detta. I mitt exempel, om skede
existerar då justera
kommer med största säkerhet att ha ett värde; de Skede
har alltid en justera
inställning, även om det är standardvärdet.
Vanligtvis ser du något av följande:
myArray: [? saker i matrisen? ] someSprite: [object Sprite] someSprite.stage: null Fel # 1009:?
Vilket borde leda dig till att skede
egendom är null
, och nu kan du gå om att fixa den.
Den enklaste lösningen är att pakka upp det motsatta uttalandet i en? Om? blockera, och kör bara blocket om den ifrågavarande variabeln inte är noll. Så, förutsatt att det i vårt tidigare exempel var det faktiskt skede
det var null
, vi kunde göra något så här:
om (someSprite.stage) myArray.push (someSprite.stage.align.toLowerCase ());
Detta test - om (someSprite.stage)
- kommer att återvända Sann
om det finns ett värde (oavsett värde), och falsk
om det är null
. I allmänhet fungerar denna notation; du kan alltid använda om (someSprite.stage! = null)
om du föredrar. siffra
s presenterar en något annorlunda situation. Om siffra
har ett värde av 0
, då har det tekniskt ett värde, men testning om (someNumberThatEqualsZero)
kommer att utvärdera till falsk
. För siffra
s du kan använda isNaN ()
funktion för att bestämma om ett giltigt numeriskt värde är lagrat i en given variabel.
I alla fall är den här tekniken ett enkelt sätt att skjuta upp felet. Om den variabel vi vill utföra en operation på inte är inställd, gör inte operationen. Om det inte finns någon god dryck i vårt glas, hämta inte glaset. Enkelt nog.
Men det här är bara möjligt om logiken är frivillig. Om logiken är nödvändig kan du kanske ge ett standardvärde till den ifrågasatta variabeln före felningsoperationen. Till exempel, om myArray
har potential att vara null
, men det är absolut nödvändigt att det inte är det vi kan göra här:
om (! myArray) myArray = []; myArray.push (someSprite.stage.align.toLowerCase ());
Detta kontrolleras först för att se om arrayen är null
. Om det är, initiera det till en tom array (en tom array är ett giltigt värde. Det kan vara tomt, men det är en array och inte null
) innan du kör den konstruerade koden. Om det inte är det null
, Hoppa direkt till den konstruerade koden. I verkliga termer, om vårt glas är tomt fyller du det med en god dryck innan du tar upp den.
Dessutom, om myArray
är en instansegenskap för den klass där den här koden körs, kan du ganska säkert försäkra ett giltigt värde genom att initialisera dina egenskaper när objektet initierar.
Vad händer om logiken krävs, men den ifrågavarande variabeln är inte så lätt under vår kontroll? Till exempel, om vår konstruerade kodkod krävs, men den tvivelaktiga variabeln är someSprite.stage
? Vi kan inte bara ställa in skede
fast egendom; som styrs internt till Display
och är skrivskyddad för oss bara dödliga. Då kan du behöva bli häftig och läsa nästa steg.
null
skedeDet finns förmodligen ett oändligt antal scenarier där en viss variabel eller egenskap kan vara null
. Självklart kan jag inte täcka dem alla i en snabb tips. Det finns en viss situation, men det växer upp igen och igen.
Låt oss säga att du skriver en kod som ser ut så här:
public class QuickSprite utökar Sprite public function QuickSprite () stage.addEventListener (MouseEvent.MOUSE_MOVE, onMove); privat funktion onMove (e: MouseEvent): void var färg: ColorTransform = new ColorTransform (); color.color = stage.mouseX / stage.stageWidth * 0xFFFFFF; this.transform.colorTransform = color;
En annan konstruerad bit kod (som kan inducera anfall - anser sig varna), men i princip är tanken att du har en Sprite
underklass och du ställer in det som klass för ett klipp på scenen, med hjälp av Flash IDE.
Du bestämmer dock att du vill arbeta med dessa QuickSprite
s programmatiskt. Så du försöker detta:
var qs: QuickSprite = ny QuickSprite (); addChild (qs);
Och du får det förbannade felet 1009. Varför? Eftersom i QuickSprite
konstruktör, du kommer åt skede
egendom (ärvt från Display
). När objektet skapas helt med kod, är det inte på scenen vid den punkten som den här koden körs, vilket betyder skede
är null
och du får felet. De QuickSprite
läggs till nästa linje, men det är inte tillräckligt snart. Om förekomsten skapas genom att dra en symbol ut ur biblioteket och på scenen, så finns det lite magi på jobbet bakom kulisserna som ser till att förekomsten är på scenen (det vill säga skede
egendom är inställt) under konstruktören.
Så här är vad du gör: du testar förekomsten av ett värde för skede
. Beroende på resultatet kan du antingen köra uppställningskoden direkt eller konfigurera en annan händelseläge för när QuickSprite
får läggas till scenen. Något som det här:
offentlig funktion QuickSprite () if (stadium) init (); annat this.addEventListener (Event.ADDED_TO_STAGE, init); privatfunktion init (e: Event = null) this.removeEventListener (Event.ADDED_TO_STAGE, init); stage.addEventListener (MouseEvent.MOUSE_MOVE, onMove);
Om vi flyttar skede
linje till en annan funktion, och bara ringa den funktionen när vi har ett stadium, då är vi inställda. Om skede
finns från början, fortsätt och spring i det()
direkt. Om inte, använder vi i det()
som händelse lyssnare funktion för ADDED_TO_STAGE
, vid vilken tidpunkt kommer vi att ha ett scenvärde och kan köra koden. Nu kan vi använda klassen för att antingen ansluta till IDE Sprite eller helt programmässigt.
Det fungerar också bra i dokumentklasser. När du kör en SWF i sig har dokumentklassen omedelbar tillgång till skede
. Om du laddar den SWF-filen till en annan SWF, körs koden i dokumentklassens initialiseringsstack innan den inmatade SWF läggs till i teckenfönstret. En liknande skede
-kontrollticket låter dig arbeta med SWF som både en fristående bit och som en SWF laddad i en innehållande SWF.
Tack för att du läste den här snabba tipsen! Jag hoppas att du är upplyst lite om hur fel 1009 inträffar, och hur du kan felsöka det. Håll dig klar för mer snabba tips om andra vanliga fel.