Snabbtips Så här debuggar du ett AS3-fel # 1063

Det är dags för en annan debugging Quick Tip. Vi fortsätter vårt fokus på specifika (och vanliga) fel som brukar stymiera mindre erfarna ActionScripters. I det här fallet täcker vi fel # 1063, räkningen räknas felaktigt fel.


Steg 1: Att vara dålig

Låt oss först skapa en situation där felet uppstår. Öppna ett nytt Flash-dokument (förutsatt att du följer med Flash Pro CS3 +, annars kan det här provet enkelt anpassas till ett Flex-projekt). Öppna Script Editor och ange följande kod:

 importera flash.events.MouseEvent; funktion onClick (): void trace ("click.");  stage.addEventListener (MouseEvent.CLICK, onClick);

Du kan förmodligen upptäcka problemet redan; Om du har spenderat någon tid på att skriva ActionScript 3, har du funnit dig själv att skriva händelsehanterare. Om inte, känner du inte dåligt - vi ska gå över allt med tiden.

Om du kör Flash-filen som det är och sedan klickar på scenen kommer du att producera följande felmeddelande om körtid:

 ArgumentError: Error # 1063: Argument count mismatch på Untitled_fla :: MainTimeline / onClick (). Förväntad 0, fick 1.

Och vi kommer aldrig till spårämnet att skall har kört efter att ha klickat.


Steg 2: Bryta ner det

Så vad händer? I det här fallet är Adobes verbiage för felet egentligen inte så dåligt, och om du har blivit van vid att analysera ett felmeddelande om körtid, kan dess innebörd vara ganska tydligt. Men inte alla är lika smarta som dig, så här är uppdelningen för alla andra.

Argument -- Detta är något obekväm information, men det visar det specifika Fel klass som kastades. Vi har något som inte är så allmänt som enkelt Fel, och vi har angett en specifik kategorisering av fel som är förknippat med argument (till funktioner och metoder).

Fel # 1063 -- Här ger vi bara det formella felnumret, liksom alla bra run-time-fel. Du kan använda denna kod för att lättare hitta den i Adobes feldokumentation för körtid.

Argumenträkningen matchar inte? -- I mer proletära termer fanns det felaktiga antalet argument som skickades till en funktion. Vilken funktion? Dess?

? på Untitled_fla :: MainTimeline / onClick (). -- Detta identifierar helt enkelt den funktion som fick fel antal argument.

Förväntad 0, fick 1. -- Vi får lite extra information i den här felbeskrivningen. Detta detaljerar räknefelkopplingen. Denna fras kommer att förändras beroende på det specifika felets art, men i vårt fall säger det att funktionen skrivits utan några argument i signaturen, men ett enda argument skickades till det ändå.

Flash gillar sina ankor i rad. Så märker den denna avvikelse och beslutar att kasta en raserianfall fel, eftersom det hellre skulle du (utvecklaren) räkna ut vad som gick fel än att du helt enkelt ignorerade problemet. Det här är bra, för om felräkningen gick åt andra håll (förväntad 1, fick 0), då hade vi fastnat utan argument för en önskad parameter, och funktionen skulle göra att hunden vet vad.


Steg 3: Problemets rota

Felets art bör vara tydlig vid denna tidpunkt, men du kanske fortfarande undrar varför det inträffade alls. Var kom det överflödiga argumentet från?

Argumentet är inte exakt överflödigt. Det förväntas faktiskt, eftersom vi har anslutit vår funktion till att vara en händelselysare. Händelsessystemet i Flash har tanken på en händelseobjekt som inkapslar aspekter av händelsen som inträffade. Detta objekt skickas till lyssnarfunktionen som enda argument. Så vi förväntat 0 eftersom vi skrev vår funktion utan några parametrar, men vi fick 1 eftersom händelsespelaren skickades längs ett händelseobjekt.

Nu kanske du undrar varför kompilatorn inte fångat det här felet. Det är sant: om du skrev detta:

 funktion sayClick (): void trace ("click.");  sayClick (42);

Då kommer SWF inte ens att kompilera, för att du får det här felet:

 1137: Felaktigt antal argument. Förväntad högst 0.

Skillnaden är att i det senare exemplet har vi faktisk kod som kallar funktionen med fel antal argument. Det vill säga, vi skrev raden ner som kallar funktionen felaktigt. Kompilatorn kan se på linjen som definierar funktionen och linjen som kallar funktionen, och jämför dem för avvikelser och larm larmet när de uppstår.


Bild av Alan / Falcon

I det ursprungliga exemplet finns det emellertid ingen kod kod nedskriven som bokstavligen kallar funktionen med namn. Istället kallas funktionen som referens. När vi lägger till händelseslyssnaren passerar vi in ​​i funktionen, och vid den tiden är det en variabel, inte ett funktionssamtal. Denna referens lagras av händelsepassatören och körs sedan dynamiskt när händelsen inträffar (det är en riktig översikt över hur händelsessystemet fungerar, men vi har inte tid att gå djupare). Så, linjen kod som i slutändan kallar den felaktiga funktionen är en ganska generisk kodlinje som använder indirection för att få jobbet gjort, och därför är det något mycket svårare för kompilatorn att fånga.

(Enligt min åsikt kunde Adobe åtminstone registrera addeventlistener linje vid sammanställningstid och leta efter funktionsreferensen med namn. Om den hittar en match kan den kontrollera funktions signaturen för ett korrekt händelseargument och skapa fel i enlighet därmed. Det kan fortfarande inte göras på ett idiotiskt sätt, men det kan gå långt för att fånga dessa fel innan du faktiskt kör SWF.

Huvudpunkten är dock att det här körtidsfelet har en kompileringstids motsvarighet, men att run-time-felet uppträder när funktionen kallas av referens och inte direkt med namn.


Steg 4: Fixera hålet

Regnet kommer in när vi kallar funktionen genom referens och har en skillnad i antalet argument. Vi har vanligtvis två alternativ: vi kan ändra samtalet eller ändra funktionens argument. I det här exemplet kan vi inte ändra samtalet, som det händer inom EventDispatcher, kod som vi inte har tillgång till. Det får oss att ändra argumenten.

Detta har återigen två alternativ. Först kan vi helt enkelt lägga till argumentet. Detta leder upp antalet argument och härifrån kommer allt att vara copacetiskt. Vi behöver inte använda argumentet, vi behöver bara ha funktionen? Fånga? det när det heter.

 funktion onClick (e: MouseEvent): void 

Det andra alternativet är att, igen, lägga till argumentet (inget sätt runt det, jag är rädd). Men om du ursprungligen skrev funktionen som en vanlig funktion och inte en händelseloggare, och ringer den från någon annanstans i din kod utan argument, kan du uppskatta denna variation. Gör argumentet valfritt och skriv det till null:

 funktion onClick (e: MouseEvent = null): void 

Detta kommer att fungera bra med händelsessystemet: det skickas ett händelseobjekt och kan fånga det. Det fungerar också bra med din befintliga kod; Om inget argument skickas används parameterns standard och funktionen fortsätter.


Steg 5: Återuppringningar

Observera att det här felet inte är begränsat till händelsehörare, men det är förmodligen det vanligaste kontextet där du kommer att uppleva det. I slutändan är det användningen av funktioner som lagras i variabler, i motsats till namnet, som leder till felet. Så här fungerar händelsessystemet. Vi kan omarbeta det ursprungliga exemplet för att producera mer eller mindre samma fel, bara utan klicket:

 funktion sayMyName (namn: String): void trace ("Hej," + namn);  var funcRef: Funktion = sayMyName; funcRef ();

Återigen kommer vi förbi kompilatorfelet eftersom vi har ett lager av indirection mellan funktionsdefinitionen och funktionssamtalet. Således får vi run-time-felet (förväntat 1, fick 0).

Det är dock inte alltid det här klippet och torrt. Om du utnyttjar återuppringningar i din kod kan du falla i byte till fel 1063. Återuppringningar är typ av händelsehörare, bara det finns ingen formell, inbyggd mekanism för att implementera dem. De är i grunden bara funktioner som du skickar genom referens, som lagras (antingen tillfälligt eller långsiktigt) av någon annan process, som sedan kallar återuppringningsfunktionen vid den lämpliga tiden.

Tweeningmotorer implementerar vanligtvis dessa. Vissa går till ett mer formellt händelsedrivet system, men TweenLite använder till exempel återuppringningar för att ta emot meddelanden om intervallets framsteg. Denna rad:

 TweenLite.to (someClip, 1, onComplete: tweenFinished, onCompleteParams: [42, "answer"]);

? skulle kalla en funktion som heter tweenFinished i slutet av intervallet, passerar två parametrar till funktionen. Denna teknik är i slutänden mer flexibel än händelser, eftersom du inte är begränsad till bara det enskilda händelseobjektet som en parameter. Men det ger sig till liknande sårbarheter mot felet 1063 på grund av vilken typ av överföringsfunktioner som hänvisas.


Det är allt

Det bryter upp en annan Debugging Quick Tip. Tack för att du läste, och jag hoppas du lärde dig något under vägen!