screencast

Välkommen till del sex av Mobiletuts + Början iOS Development-serien. Denna avbetalning kommer att täcka Xcode debugging-principer. Det kommer att innehålla en kort mängd programvarufelningsteori och en övningsapplikation för att visa användningen av brytpunkter och Xcode debugger. Artikeln kommer att sluta med några allmänna tips och bästa praxis, samt en lista över användbara resurser som är tillgängliga för dig att fortsätta din inlärning.

screencast:

Problem med att titta på media ovan? Få den fulla, högkvalitativa versionen av den här videon i sitt ursprungliga, MOV-format.

Tutorial Transcript:

Istället för att bygga en ny applikation speciellt för denna handledning har jag tagit programmet FortuneCrunch som vi skapade i del två i denna serie och jag har infört ett antal olika fel i källkoden. Hämta BrokenCrunch, den trasiga versionen av FortuneCrunch som följer med det här inlägget, att följa med när jag visar hur man använder Xcode-felsökningsverktygen.

Debugging Theory

Innan vi faktiskt debuggerar BrokenCrunch, låt oss prata ett ögonblick om programvarufelningsteori. Generellt kan programvarufel (även kända som "buggar") kategoriseras enligt följande:

  • Kompileringstiden Fel
  • Fel på felaktigheter
  • Logiska fel

Kompileringstiden Fel

Som namnet antyder inträffar ett sammanställningsfel när du försöker kompilera programkällkoden. I Xcode händer detta när du väljer "Build and Run" eller "Build and Debug" för att starta din applikation på enheten eller simulatorn. När ett kompileringsfel uppstår kommer det att förhindra att din ansökan startas. Som vi kommer att se kan fel i denna kategori antingen uppstå från otillräckliga eller felaktiga syntaxutlåtanden eller från problem som uppstår i länkfasen i din applikationsbyggnad. I allmänhet är kompileringsfel det enklaste av de tre kategorierna att lösa eftersom kompilatorn vanligen kommer att utfärda ett meningsfullt felmeddelande eller varningsmeddelande som kommer att varna dig för problemets art.

Fel på felaktigheter

Fel vid körning uppstår efter att din ansökan har sammanställts och lanserats i simulatorn eller på en enhet. En programkrasch eller minnesläcka som uppstår som en följd av dålig objektminnehantering är ett exempel på ett fel i driftstiden.

Logiska fel

Ett logiskt fel inträffar under en applikations körningstid och resulterar i oväntat eller oönskat applikationsbeteende som strider mot programutvecklarens eller projektets intressenters avsedda resultat. Ett bra exempel på ett logiskt fel är en matematisk formel som har implementerats felaktigt. Tänk på den pythagoranska ståndpunkten:

Om en programvaruutvecklare oavsiktligt implementerade denna formel som:

Resultatet skulle vara ett logiskt fel, men det skulle troligtvis inte leda till att programmet skulle krascha. Det är det som gör logiska fel så farliga: Applikationen kan tyvärr springa "buggfri" till utvecklaren samtidigt som den producerar ogiltig eller oönskad produktion.

Debugging BrokenCrunch

Med denna teoretiska kunskap på plats, öppna BrokenCrunch och låt oss komma igång. När du har laddat in vårt provprogram, välj Bygg> Bygg och Debug. Du märker att programmet inte startar, och kompilatorn har genererat ett antal fel. För att se resultaten av försöket samla, välj Bygg> Bygga resultat.

Om du väljer de angivna felen tar du dig direkt till koden där felet rapporteras. En viktig sak att tänka på är emellertid att antalet fel som rapporteras av kompilatorn och linjenummerna för dessa fel ska anses vara "bästa gissningen" av vad som är fel med din ansökan, inte ett avgörande uttalande.

Faktum är att ett enkelt syntaxfel kan resultera i att flera fel rapporteras av kompilatorn som inte är relaterade till problemet. Som ett exempel, ta en titt på "Förväntad konsol före" setImage "" fel linje. Om du undersöker den aktuella raden ska du upptäcka att syntaxen är perfekt. Som det visar sig är problemet här inte på raden som rapporterats, men raden precis ovanför den. Ser du problemet?

De NSLog () uttalandet avslutades inte med en semikolon. Det betyder att kompilatorn inte vet att du tänkte avsluta raden efter den sista parentesen och ser allt från NSLog till den slutliga stängningsfästet och halvkolonet efter UIControlStateNormal som ett uttalande.

Lägg till semikolon för att slutföra NSLog påstående:

 NSLog (@ "I crunchCookie"); 

Spara källfilen och klicka på "Build and Debug" igen. Två av de tre fel som ursprungligen visades skulle nu lösas.

Välj sedan "Ingen förklaring av egendom" fel. Som du kan se rapporterar detta fel att den egendom vi försöker syntetisera inte existerar. För att verifiera detta, öppna filen FortuneCrunchViewController.h där egenskapen borde ha deklarerats. Om du undersöker linje 17 är syntaxen korrekt, men vi har en otillbörlig matchning mellan egenskapen vi har förklarat och den vi försöker syntetisera. Objektiv-C är ett skiftlägeskänsligt språk, vilket betyder att "C" i cookie måste aktiveras för att matcha egenskapen som vi försöker syntetisera. Uppdatera egenskapsdeklarationen i huvudfilen som ska läsas:

 @property (nonatomic, behåll) IBOutlet * fortuneCookieButton; 

Spara källfilen och bygga och felsöka igen. Den här gången, snarare än att öppna byggresultatet från Bygg> Bygg och Debug, klicka bara på felikonen längst ned till höger om Xcode.

Ett steg framåt, fyra steg tillbaka. Felet med att syntetisera egenskapslinjen är borta, men vi har en helt ny lista över fel. Vad hände?

Det här är en bra tid att ta del av de olika faserna som visas i fönstret Bygga resultat:

Observera att vi har en varning under "Kompilera" av byggresultatets resultat. Detta är samma avsnitt som våra tidigare fel rapporterades in. Nu när de tre föregående fel har lösts har vi kunnat gå vidare från kompileringsfasen till länkfasen i vår applikationsbyggnad, och alla nya fel är länkfel. När du stöter på ett länkfel beror det vanligtvis på att du försöker använda funktioner från ett ramverk som du inte faktiskt har inkluderat i din ansökan. I det här fallet refererar Byggresultatet till en funktion som heter _UIApplicationMain i main.o filen. Main.o är den sammanställda maskinkodversionen av main.m. Låt oss ta en titt på källkoden i den filen. På rad 13 kan du se ett funktionssamtal till UIApplicationMain:

 int retVal = UIApplicationMain (argc, argv, nil, nil); 

UIApplicationMain är en central funktion för varje iOS-applikation, men hur kan du lära dig mer om det och ta reda på vilken ram det ingår i? Lyckligtvis kommer iOS SDK med stor dokumentation. Om du håller ner alternativet (eller alt) och dubbelklickar på funktionsnamnet startar du en abstrakt från den officiella iOS SDK-dokumentationen som diskuterar denna funktion. Klicka på ikonen "bok" längst upp till höger för att visa den fullständiga dokumentationen som finns tillgänglig. Du kan se att så lanserade funktionsreferensdokumentationen för UIKIT-ramverket. Bingo, vi har vår saknade ram. Men innan vi lägger till ramverket för projektet, låt oss undersöka en annan metod som du kunde ha använt för att bestämma ursprunget till UIApplicationMain.

Stäng dokumentationsfönstret. Håll nu kommandoknappen nertryckt och dubbelklicka på UIApplicationMain fungera. Du tittar nu på källan till UIApplication.h, huvuddeklarationsfilen som innehåller UIApplicationMain funktionsdeklaration. Om du bläddrar till toppen av fönstret ser du att den här filen importerar flera andra UIKit-rubriker, och att kommentaren högst upp innehåller "UIKit" ramnamn.

Låt oss fortsätta att lösa dessa länkfel genom att inkludera UIKIT-ramverket. För att göra det, högerklicka eller kontrollera klicka på rammen Ramar i rutan Grupper och filer och välj lägg till> befintliga ramar. Hitta UIKit-ramen och klicka på "Lägg till." För att testa vårt arbete, välj Build och Debug igen.

Som du kan se, lanserade simulatorn framgångsrikt och vi kan se vår ansökan. Det innebär att vi har löst alla kompileringsfel i vår ansökan.

Fortsätt och klicka på förmögenhetskakan ... som du kan se, klickar du på kakan ett fel i körtiden och programmet har kraschat. Meddelandet som visas längst ned till vänster på Xcode-skärmen är inte till stor hjälp, så låt oss titta närmare genom att öppna konsolen.

Konsolen visar både en samtalstack av vad som hände i vårt programutförande vid kraschtiden, samt en mer detaljerad förklaring: "Avslutande app på grund av oavsett undantag ... FortuneCrunchViewController cookieCruncher: okänd väljare skickad till exempel." Det här meddelandet innebär att vår knapp ringer fel väljare för händelsen som vi avskedade genom att klicka på kakan. Eftersom gränssnittet för FortuneCrunch byggdes i Interface Builder, låt oss öppna XIB-filen Interface Builder för "FortuneCrunchViewController" för att titta närmare.

Välj cookie-knappen och kontrollera klick eller högerklicka för att visa en lista över anslutna åtgärder:

Du kan se att händelsen Touch Up Inside refererar till ett mål som inte existerar, indikerat med den gula texten. Ta bort det obefintliga "cookieCruncher" -målet och koppla igen touchUpInside till File Owner genom att välja "crunchCookie" -målet som visas i rullgardinsmenyn. Spara ditt arbete i Interface Builder, växla tillbaka till Xcode och starta om programmet.

Om du klickar på förmögenhetskakan igen resulterar det i ett fel i driftstiden. Den här gången är konsolmeddelandet inte så användbart, det visar bara "EXC_BAD_ACCESS".

Ta en titt på byggresultaten genom att välja Bygg> Bygga resultat. Visste du varningen tidigare? Kompilatorns varningar är ofta en indikation på ett potentiellt körtidsfel, men eftersom det inte finns något felaktigt med den faktiska syntaxen för den linje som varningen utfärdas för kan kompilatorn fortfarande bygga programmet framgångsrikt. Självklart finns det tider när en kompilatorns varning är en "falsk flagg" och kommer inte att resultera i ett run-time-fel, men upp till 95% av tiden, om kompilatorn har utfärdat en varning gör du något fel.

Klicka på varningen för att hoppa till raden i källkoden där den uppstod.

Varningen hänvisar till inkompatibla pekartyper. Ser du problemet? Metoden ImageNamed förväntar sig ett NSString-objekt, men denna kodlinje tillhandahåller metoden med en bokstavlig C-stilsträng. Lägg till i "@" -symbolen för att göra detta till en Objective-C-sträng:

 [FortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-closed.png"] forState: UIControlStateNormal]; 

Spara dina framsteg och kör programmet igen.

Den här gången, när du klickar på förmögenhetskakan, uppstår du ett logiskt fel: programmet kraschar inte och etiketten "Happy iPhone Hacking" visas som förväntat, men bakgrundsbilden förblir som den stängda kakan.

För att fixa det här, låt oss ta en titt på den funktion som är ansvarig för övergången: (IBAction) crunchCookie. Linje 19 ansvarar för att ändra bakgrundsbilden och du kan se att den ställer in den nya bakgrundsbilden till "cookie-closed.png". Om du tittar på cookie-closed i mappen Resurser ser du att det här är faktiskt samma bild som visas när appen laddas först. Vi måste ändra den linjen för övergång till "cookie-crunched.png":

 [FortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-crunched.png"] förState: UIControlStateNormal]; 

Bygg och kör programmet igen ... och nu knackar du på cookieresultaten i den förväntade bakgrundsbilden med etiketten som visas korrekt.

grattis! Du har just gått igenom processen för att fixa kompileringstidsfel, körtidsfel och logiska fel i en applikation. Under tiden har vi knappt knackat in i de kraftfulla felsökningsverktygen som är tillgängliga för dig med Xcode.

För att fortsätta utforskningen av de mer avancerade felsökningsverktygen som är tillgängliga, låt oss försöka utvidga programmet FortuneCrunch för att göra det lite mer intressant. Snarare än att visa den statiska strängen "Happy iPhone Hacking!" Varje gång kakan är crunched, låt oss bygga en rad flera NSString värden som kan visas.

Byt tillbaka till Xcode och öppna filen FortuneCrunchViewController.h. Lägg till följande data medlem:

 NSArray * förmögenheter; 

Denna array används för att hålla våra slumpmässiga förmögenhetssträngar.

Lägg nu till följande metod signatur:

 -(NSString *) genereraRandomFortune; 

Denna rad kommer att deklarera en ny metod i vår klass som kommer att användas för att välja en slumpmässig förmögenhet från vår förmögenhet.

Nästa byt till FortuneCrunchViewController.m. Eftersom denna klass kommer att initieras från vår XIB-fil, måste vi åsidosätta initWithCoder metod och allokera arrayen som vi deklarerade i .h-filen, initiera den med några nya förmögenheter:

 -(id) initWithCoder: aDecoder self = [super initWithCoder: aDecoder]; om (själv) fortunes = [[NSArray alloc] initWithObjects: @ "Den som kastar smuts förlorar marken.", @ "En sluten mun samlar inga fötter.", @ "Hjälp! Jag är en fånge i ett bageri! ", Nil];  återvänd själv  

Nu när vi har skapat en ny NSArray, glöm inte att släppa den i dealloc metod:

 -(void) dealloc [fortunes release]; 

Låt oss fortsätta att koda generateRandomFortune fungera:

 -(NSString *) genereraRandomFortune int selected_index = arc4random ()% 3 * 10; returnera [förmögenheter objectAtIndex: selected_index];  

Dessa linjer genererar helt enkelt ett nytt, slumpmässigt indexnummer som vi kommer att använda för att returnera motsvarande förmögenhetsträng.

Ändra ändå crunchCookie metod för att använda en av våra slumpmässiga förmögenheter snarare än den statiska texten "Happy iPhone Hacking!":

 fortuneLabel.text = [self generateRandomFortune]; 

Bygg och kör programmet efter att ha sparat ändringarna. Om du klickar på kakan skapar du ett körtidsfel. För att ta reda på varför det här händer, kommer vi att använda Xcode debugger och anpassade brytpunkter.

En brytpunkt är en flagga som signalerar till din ansökan att programkörning ska "pausa" när raden med brytpunkten uppnås. Om du kör din applikation i "Bygg och Debug-läge" kan du använda brytpunkter. För att ställa in en brytpunkt, klicka helt enkelt på redigeraren "gutter" på den linje som du vill utlösa en brytpunkt. För att ta reda på vad som händer i vår ansökan ska vi ställa in vår brytpunkt på NSLog linje, precis efter crunchCookie-metoden heter:

Bygg och felsök ansökan med denna nya brytpunkt på plats.

När applikationen laddas klickar du på kakan. Om du tittar längst ner till vänster om Xcode ser du statusmeddelandet "Stoppat vid brytpunkt 1". Det betyder att felsökaren har lyckats sluta programkörning vid den brytpunkt du ställt in. Du kommer också märka att en röd pil indikerar den nuvarande raden av körning där debuggeren har "pausat" programmet.

Så vad kan du göra med debugger? Mer än kan omfattas av en enda handledning. Det finns dock tre grundläggande åtgärder du kan vidta vid denna tidpunkt: Gå över, gå in och gå ut. Alla dessa alternativ är tillgängliga för dig från fältet för kodfelsökning.

Om du trycker på "steg över" -knappen på koden debugger-menyn kommer du märka att programkörning fortsätter till nästa rad. "Steg över" kommer helt enkelt fortsätta att exekvera en rad i taget inom den aktuella metoden, men det kommer inte att följa kodkörningen om det gafflar till en annan metod. Om du verkligen vill följa kodkörningen i andra metodsamtal i din kod måste du använda "steg in" -knappen.

Som ni kan se har vi faktiskt tagit oss in i generateRandomFortune metod, vilket är exakt vad vi vill ha. Klicka på "Steg över" igen för att se vad som händer när arc4random () kallas. Skulle det inte vara trevligt om vi visste vad varianten selected_index just har ställts till? Lyckligtvis kan vi! En av de bästa egenskaperna med att använda debugger är möjligheten att helt enkelt mus över variabler för att snabbt se deras värde.

Det är klart att chosen_index värdet är mycket större än längden på vår array. Till skillnad från i vissa andra programmeringsspråk kommer den randomiseringsfunktion vi använder att returnera ett heltal, så det behöver inte konverteras från ett decimaltal till ett heltal genom att multiplicera värdet med 10. Uppdatera raden att läsa:

 int selected_index = arc4random ()% 3; 

Vi är redo att göra ändringar i denna funktion, så använd "Step Out" -knappen för att avsluta denna underfunktion och återgå till crunchCookie. Observera att även om vi inte såg det, utfördes resten av funktionen som vanligt.

Slutligen notera på "Aktivera / avaktivera" brytpunktsknappen och "Fortsätt utförande" -knappen på menyfältet i kodavkodaren. "Fortsätt Exekvering" tillåter helt enkelt att programkörning fortsätter som vanligt. Du kan tänka på det som "unpause" -knappen. Fortsätt och tryck på det här nu.

Innan vi fortsätter att stänga av brytpunkter finns det ytterligare ett problem att ta itu med: Det du just har fått kallas "in-code debugger". Det är mycket kraftfullt, men det finns också två andra felsökningslägen som är tillgängliga för dig: det fullständiga felsökningsfönstret och mini-debugger-perspektivet.

För att komma åt felsökningsfönstret, klicka på ikonen "debugging" i menyfältet i kodfelsökaren. Det här fönstret har betydligt mer information än in-code debugger. Till vänster har du en stackspår som visar sammanhanget för programkörning (du har också möjlighet att välja från någon av de aktuella språken). Till höger kan du se en snabb visning av de olika variabler som för närvarande hålls i minnet. Om du väljer en annan samtalstapelsignatur ändras din syn i Debugger. Du kan ändra fönstret Debugger-fönster genom att gå till Kör> Debugger Display.

Slutligen är mini-debugger ännu ett felsökningsperspektiv tillgängligt för dig. Jag använder sällan detta perspektiv, men det är tillgängligt för dig från Kör> Mini-Debugger.

Eftersom vi bara löst det fel som introducerades i vår slumpmässiga förmögenhetskod behöver vi inte längre felsökaren vara på. Byt av brytpunkter. Innan vi bygger programmet igen, låt oss justera stilsorten för vår förmögenhetskod.

Öppna gränssnittsbyggare, välj etiketten och ändra teckensnittet i inspektören till Arial Black, 9 poäng och välj sedan rutan "Anpassa till passform" och ändra lägsta teckensnittstorlek till 6 poäng. Bygg och kör nu vårt projekt igen.

Voila! Vår ansökan fungerar nu som vi tänkte.

Felsökningstips och tricks

Nu när du har introducerats till grunderna för att använda Debugger i Xcode, bör du överväga att tillämpa följande riktlinjer i ditt vardagliga utvecklingsarbete:

Test i både simulatorn och på en fysisk enhet

Medan simulatorn är en användbar metod för att testa en applikation under utvecklingsfasen av din produkt, är det inte ett ersättningsalternativ för testning på en fysisk enhet. Detta beror på att simulatorn och en iOS-enhet skiljer sig på viktiga och grundläggande sätt. Till exempel kör simulatorn uppenbarligen inom OS X, och filsystemet på OS X är inte skiftlägeskänsligt. Filsystemet på IOS är dock skiftlägeskänsligt. Så hänvisar till filen kaka-CRUNChed.png, istället för kaka-crunched.png, kommer att fungera bra i simulatorn men misslyckas på en faktisk iOS-enhet. Ett annat viktigt övervägande är att simulatorn har mycket mer minne tillgängligt än en faktisk enhet, och detta faktum påverkar ofta användarupplevelsen mycket. Slutligen är inte alla standardprogram som skickas med iOS tillgängliga i simulatorn, inklusive programmen Maps och App Store. Det betyder att du inte kommer att kunna testa kod som genererar körriktningar med appen Maps eller överpromotorer i App Store i simulatorn. Dessa är bara några av skillnaderna som finns. Jag rekommenderar starkt testning på så många fysiska iOS-enheter som kör så många olika riktade versioner av iOS som möjligt.

Använd Clang Static Analyzer

Clang Static Analyzer är ett speciellt C / Objective-C statiskt analysverktyg som skickas med Xcode. Det här verktyget kan analysera din kod för fel eller inkonsekvenser som annars skulle gå obemärkt.

Medan detaljerna om hur analysatorn fungerar är bortom tillämpningsområdet för denna artikel är det lyckligtvis mycket lätt att använda det. För att utföra en statisk analys av din kod, välj bara Bygg> Bygg och analysera från Xcode-byggmenyn.

Om du vill lära dig mer om hur alternativet "Bygg och analysera" fungerar kan du läsa om Statisk kodanalys och Clang Static Analyzer online.

Ange en global brytpunkt på objc_exception_throw

I denna handledning lärde vi oss om hur brytpunkter fungerar genom att ställa in projektspecifika brytpunkter i vår kod. Förutom projektspecifika brytpunkter kan Xcode du också ställa in "globala" brytpunkter som gäller för alla iOS-projekt du skapar i Xcode. Ställa in en global brytpunkt på objc_exception_throw tillåter dig att starta debuggeren automatiskt när ett undantag (en typ av körtid) uppstår. För att läsa mer om fördelarna med detta tillvägagångssätt och hur man implementerar det i kod, se min iOS Quick Tip om objc_exception_throw och globala brytpunkter.

Behandla varningar som fel

Som tidigare nämnts i den här handledningen måste de flesta kompilatorvarningar som utfärdas lösas innan du startar ditt projekt, och det borde verkligen aldrig vara ett scenario när kod som genererar kompilatorvarningar måste förblir oförändrad för att en ansökan ska fungera korrekt. Följaktligen rekommenderar vissa programmerare att behandla alla kompilatorvarningar som fel, vilket tvingar dem att lösas som en del av det normala utvecklingsprocessen.

För alla utom några få fransfall stöder jag den här idén, och Xcode gör det enkelt att implementera. Gå till Projekt> Redigera projektinställningar och välj sedan fliken Bygg. Skriv "Behandla varning" i sökfältet och du får se ett booleskt värde som heter "Behandla varningar som fel." Markera den här rutan för att aktivera funktionen.

Bygg och validera innan App Store-inlämning

Ett annat steg du kan vidta för att öka oddsen för att din ansökan accepteras första gången du skickar in den till iTunes Store är att aktivera flaggan "Bygg och validera" i projektbyggnadsinställningarna. Skriv "validera" i sökrutan på fliken Projektinställningar och välj sedan "Validera byggprodukt". Det här alternativet kör några av de tester som utförts av Apple-granskarna, vilket möjliggör att du eventuellt kan förhindra en App Store-avvisning. Det bör noteras att kontroll av den här rutan inte är en garanti för att din app kommer att övergå till App Store-recension, men det är bättre än ingenting.

Ytterligare felsökningsverktyg

Förutom konsolen, bygga resultat och Debugger finns det några andra bra felsöknings- och optimeringsverktyg som du borde vara medveten om i din utvecklingsinsats. För ytterligare läsning, ta en titt på dokumentationen för följande verktyg:

  • instrument
  • Haj
  • Top / Spin Control
  • Stor topp

Slutsats

Det här har varit en virveltur i felsökning med iOS SDK. Det finns fortfarande mycket mer som kan göras, men förhoppningsvis har den här lektionen varit tillräckligt för att hjälpa dig att snabbt lösa buggarna i dina egna applikationer och skriva bättre programvara! Om du vill höra mer om några av de avancerade verktygen som diskuteras i denna handledning, till exempel Instruments, Shark eller SpinControl, eller om du vill höra mer om felsökning generellt, lämna en kommentar nedan och låt mig veta!