I Mål-C er det to typer feil som kan oppstå mens et program kjører. Uventet feil er "seriøse" programmeringsfeil som vanligvis forårsaker at programmet slutter for tidlig. Disse kalles unntakene, siden de representerer en eksepsjonell tilstand i programmet. På den andre siden, forventet Feil oppstår naturlig i løpet av et programs gjennomføring og kan brukes til å bestemme suksess for en operasjon. Disse er referert til som feil.
Du kan også nærme forskjellen mellom unntak og feil som en forskjell i deres målgrupper. Generelt brukes unntak for å informere Programmerer om noe som gikk galt, mens feilene brukes til å informere bruker at en forespurt handling ikke kunne fullføres.
Kontrollstrøm for unntak og feilFor eksempel, hvis du prøver å få tilgang til en arrayindeks som ikke eksisterer, er et unntak (en programmeringsfeil), mens du ikke åpner en fil, er det en feil (en brukerfeil). I det forrige tilfellet gikk det sannsynligvis noe galt i strømmen av programmet ditt, og det burde trolig stenge kort etter unntaket. I det sistnevnte vil du fortelle brukeren at filen ikke kunne åpnes og muligens be om å prøve på nytt, men det er ingen grunn til at programmet ikke kunne fortsette å løpe etter feilen.
Hovedfordelen for Objective-Cs unntakshåndteringsmuligheter er muligheten til å skille håndteringen av feil fra feiloppdagelsen. Når en del kode opplever et unntak, kan det "kaste" det til nærmeste feilhåndteringsblokk, som kan "fange" bestemte unntak og håndtere dem på riktig måte. Det faktum at unntak kan kastes fra vilkårlig plassering eliminerer behovet for stadig å sjekke om suksess eller feilmeldinger fra hver funksjon som er involvert i en bestemt oppgave.
De @prøve
, @å fange()
, og @endelig
Kompilatordirektiver brukes til å fange og håndtere unntak, og @kaste
Direktivet brukes til å oppdage dem. Hvis du har jobbet med unntak i C #, bør disse konstruksjonene for unntakshåndtering være kjent for deg.
Det er viktig å merke seg at i Mål-C er unntak relativt langsomme. Som et resultat bør bruken av dem begrenses til å fange seriøse programmeringsfeil - ikke for grunnleggende kontrollstrøm. Hvis du prøver å bestemme hva du skal gjøre basert på en forventet feil (for eksempel ikke å laste inn en fil), vennligst se Feilhåndteringsseksjon.
Unntak er representert som forekomster av NSException
klasse eller en underklasse derav. Dette er en praktisk måte å inkapslere all nødvendig informasjon knyttet til et unntak. De tre egenskapene som utgjør et unntak er beskrevet som følger:
Navn
- En forekomst av NSString
som unikt identifiserer unntaket.grunnen til
- En forekomst av NSString
inneholder en menneskelig lesbar beskrivelse av unntaket.brukerinformasjon
- En forekomst av NSDictionary
som inneholder applikasjonsspesifikke opplysninger relatert til unntaket.Stiftelsesrammen definerer flere konstanter som definerer "standard" unntaksnavnene. Disse strengene kan brukes til å kontrollere hvilken type unntak som ble fanget.
Du kan også bruke initWithName: Grunnen: Userinfo:
initialiseringsmetode for å lage nye unntaksobjekter med egne verdier. Egendefinerte unntak objekter kan bli fanget og kastet med de samme metodene dekket i de kommende seksjonene.
La oss begynne med å ta en titt på standardhåndteringshåndteringen til et program. De objectAtIndex:
Metode av NSArray
er definert for å kaste en NSRangeException
(en underklasse av NSException
) når du prøver å få tilgang til en indeks som ikke eksisterer. Så, hvis du ber om 10th element i en matrise som bare har tre elementer, har du selv et unntak for å eksperimentere med:
#importereint main (int argc, const char * argv []) @ autoreleasepool NSArray * mannskap = [NSArray arrayWithObjects: @ "Dave", @ "Heywood", @ "Frank", null]; // Dette vil kaste et unntak. NSLog (@ "% @", [besetning objektAtIndex: 10]); returnere 0;
Når den møter et uncaught unntak, stopper Xcode programmet og viser deg til linjen som forårsaket problemet.
Avbryter et program på grunn av et uncaught unntakDeretter lærer vi hvordan vi får tak i unntak og forhindrer at programmet avsluttes.
For å håndtere et unntak, vil enhver kode som kan resultere i et unntak bør plasseres i a @prøve
blokkere. Deretter kan du fange spesifikke unntak ved å bruke @å fange()
direktiv. Hvis du trenger å utføre noen husholdningskode, kan du eventuelt plassere den i en @endelig
blokkere. Følgende eksempel viser alle tre av disse håndteringsdirektiver:
@try NSLog (@ "% @", [besetning objektAtIndex: 10]); @catch (NSException * unntak) NSLog (@ "Fanget et unntak"); // Vi vil bare tydelig ignorere unntaket. @finally NSLog (@ "Cleaning up");
Dette skal sende følgende i Xcode-konsollen:
Fanget et unntak! Navn: NSRangeException Årsak: *** - [__ NSArrayI objectAtIndex:]: indeks 10 utenfor grenser [0 ... 2] Opprydding
Når programmet møter [besetning objektAtIndex: 10]
melding, det kaster en NSRangeException
, som er fanget i @å fange()
direktiv. Innsiden av @å fange()
blokk er hvor unntaket faktisk håndteres. I dette tilfellet viser vi bare en beskrivende feilmelding, men i de fleste tilfeller vil du sikkert skrive noen kode for å ta vare på problemet.
Når et unntak oppstår i @prøve
blokk, programmet hopper til det tilsvarende @å fange()
blokkere, noe som betyr hvilken kode som helst etter unntaket oppstått vil ikke bli utført. Dette utgjør et problem hvis @prøve
blokk trenger litt rydding (for eksempel hvis den åpnet en fil, må den filen være stengt). De @endelig
blokk løses dette problemet, siden det er garantert å bli henrettet uansett om et unntak oppstod. Dette gjør det til det perfekte stedet å binde opp løse ender fra @prøve
blokkere.
Parentesene etter @å fange()
Direktivet lar deg definere hvilken type unntak du prøver å fange. I dette tilfellet er det en NSException
, som er standard unntaksklasse. Men et unntak kan faktisk være noen klasse-ikke bare en NSException
. For eksempel, følgende @å fange()
Direktivet vil håndtere et generisk objekt:
@catch (id genericException)
Vi lærer å kaste forekomster av NSException
så vel som generiske objekter i neste avsnitt.
Når du oppdager en eksepsjonell tilstand i koden din, oppretter du en forekomst av NSException
og fyll den med relevant informasjon. Deretter kaster du den ved hjelp av den passende navnet @kaste
Direktivet, som ber om det nærmeste @prøve
/@å fange
blokk for å håndtere det.
For eksempel definerer følgende eksempel en funksjon for å generere tilfeldige tall mellom et spesifisert intervall. Hvis den som ringer passerer et ugyldig intervall, kaster funksjonen en tilpasset feil.
#importereint generateRandomInteger (int minimum, int maksimum) if (minimum> = maksimum) // Opprett unntaket. NSException * exception = [NSException exceptionWithName: @ "RandomNumberIntervalException" grunn: @ "*** generateRandomInteger ():" "maksimal parameter ikke større enn minimum parameter" userInfo: null); // Kast unntaket. @throw unntak; // Returner et tilfeldig heltall. returnere arc4random_uniform ((maksimum - minimum) + 1) + minimum; int main (int argc, const char * argv []) @ autoreleasepool int resultat = 0; @try result = generateRandomInteger (0, 10); @catch (NSException * unntak) NSLog (@ "Problem! Fangst unntak:% @", [unntak navn]); NSLog (@ "Tilfeldig nummer:% i", resultat); returnere 0;
Siden denne koden passerer et gyldig intervall (0, 10
) til generateRandomInteger ()
, det vil ikke ha unntak for fangst. Men hvis du endrer intervallet til noe som helst (0, -10
), får du se @å fange()
blokkere i aksjon. Dette er i hovedsak hva som skjer under hetten når rammeklassene møter unntak (f.eks NSRangeException
oppdratt av NSArray
).
Det er også mulig å gjenkaste unntak som du allerede har tatt. Dette er nyttig hvis du vil bli informert om at et bestemt unntak oppstod, men ikke nødvendigvis vil håndtere det selv. Som en bekvemmelighet kan du til og med utelate argumentet til @kaste
direktiv:
@try result = generateRandomInteger (0, -10); @catch (NSException * unntak) NSLog (@ "Problem! Fangst unntak:% @", [unntak navn]); // Gjenta nåværende unntak. @throw
Dette gir det fanget unntaket opp til den nærmeste høyeste handler, som i dette tilfellet er toppnivåns unntakshandler. Dette burde vise produksjonen fra vår @å fange()
blokk, samt standard Avsluttende app på grunn av uncaught unntak ...
melding, etterfulgt av en abrupt utgang.
De @kaste
Direktivet er ikke begrenset til NSException
objekter-det kan kaste bokstavelig talt noen gjenstand. Følgende eksempel kaster en NSNumber
objekt i stedet for et normalt unntak. Legg også merke til hvordan du kan målrette mot forskjellige objekter ved å legge til flere @å fange()
uttalelser etter @prøve
blokkere:
#importereint generateRandomInteger (int minimum, int maksimum) if (minimum> = maksimum) // Generer et tall med "standard" intervall. NSNummer * gjett = [NSNummer nummerWithInt: generateRandomInteger (0, 10)]; // Kast nummeret. @throw gjette; // Returner et tilfeldig heltall. returnere arc4random_uniform ((maksimum - minimum) + 1) + minimum; int main (int argc, const char * argv []) @ autoreleasepool int resultat = 0; @try result = generateRandomInteger (30, 10); @catch (NSNumber * gjetning) NSLog (@ "Advarsel: Brukt standardintervall"); result = [gjett intValue]; @catch (NSException * unntak) NSLog (@ "Problem! Fangst unntak:% @", [unntak navn]); NSLog (@ "Tilfeldig nummer:% i", resultat); returnere 0;
I stedet for å kaste en NSException
gjenstand, generateRandomInteger ()
Forsøker å generere et nytt tall mellom noen "standard" grenser. Eksemplet viser deg hvordan @kaste
kan fungere med ulike typer objekter, men dette er ikke den beste applikasjonsdesignen, og det er heller ikke den mest effektive bruken av Objective-Cs unntakshåndteringsverktøy. Hvis du egentlig bare hadde tenkt å bruke den kastede verdien som den forrige koden gjør, ville du være bedre med en vanlig gammel betinget sjekk ved å bruke NSError
, som omtalt i neste avsnitt.
I tillegg er noen av Apples kjernerammer forvente en NSException
protester mot å bli kastet, så vær forsiktig med tilpassede objekter når du integrerer med standardbiblioteker.
Mens unntak er laget for å la programmører vite når ting har gått dårlige feil, er feil utformet for å være en effektiv, grei måte å kontrollere om en handling lykkes eller ikke. I motsetning til unntak, feil er designet for å bli brukt i dine daglige kontrollflyt setninger.
Den eneste tingen som feil og unntak har til felles er at de begge er implementert som objekter. De NSError
klassen innkapsler all nødvendig informasjon for å representere feil:
kode
- en NSInteger
som representerer feilens unike identifikator.domene
- En forekomst av NSString
definerer domenet for feilen (beskrevet nærmere i neste avsnitt).brukerinformasjon
- En forekomst av NSDictionary
som inneholder programspesifikke opplysninger relatert til feilen. Dette brukes vanligvis mye mer enn brukerinformasjon
ordbok av NSException
.I tillegg til disse kjernegenskapene, NSError
Lagrer også flere verdier som er utformet for å hjelpe til med gjengivelse og behandling av feil. Alle disse er faktisk snarveier til brukerinformasjon
ordbok beskrevet i forrige liste.
localizedDescription
- en NSString
inneholder den fulle beskrivelsen av feilen, som vanligvis inneholder årsaken til feilen. Denne verdien vises vanligvis til brukeren i et varselpanel.localizedFailureReason
- en NSString
inneholder en frittstående beskrivelse av årsaken til feilen. Dette brukes kun av klienter som ønsker å isolere årsaken til feilen fra sin fullstendige beskrivelse.recoverySuggestion
- en NSString
instruere brukeren hvordan å gjenopprette fra feilen.localizedRecoveryOptions
- en NSArray
av titler som brukes til knappene i feildialogen. Hvis dette arrayet er tomt, en enkelt OK knappen vises for å avvise varselet.helpAnchor
- en NSString
å vise når brukeren trykker på Hjelp Ankerknapp i et varselpanel.Som med NSException
, de initWithDomain: Kode: Userinfo
Metoden kan brukes til å initialisere tilpasset NSError
forekomster.
Et feildomene er som et navneområde for feilkoder. Koder bør være unike innenfor et enkelt domene, men de kan overlappe koder fra andre domener. I tillegg til å hindre kodekollisjoner, gir domener også informasjon om hvor feilen kommer fra. De fire viktigste innebygde feildomenene er: NSMachErrorDomain
, NSPOSIXErrorDomain
, NSOSStatusErrorDomain
, og NSCocoaErrorDomain
. De NSCocoaErrorDomain
inneholder feilkoder for mange av Apples standardmål-C-rammer; Det er imidlertid noen rammer som definerer sine egne domener (f.eks., NSXMLParserErrorDomain
).
Hvis du trenger å opprette egendefinerte feilkoder for biblioteker og programmer, bør du alltid legge dem til din egen feildomene - utvide aldri noen av de innebygde domenene. Å lage ditt eget domene er en relativt triviell jobb. Fordi domener er bare strenger, er alt du trenger å definere en strengskonstant som ikke er i konflikt med noen av de andre feildomenene i søknaden din. Apple foreslår at domener har form av com.
.
Det er ingen dedikerte språkkonstruksjoner for håndtering NSError
forekomster (selv om flere innebygde klasser er designet for å håndtere dem). De er designet for å brukes sammen med spesialdesignede funksjoner som returnerer en gjenstand når de lykkes og nil
når de feiler. Den generelle prosedyren for å fange feil er som følger:
NSError
variabel. Du trenger ikke å tildele eller initialisere det.NSError
å håndtere feilen selv eller vise den til brukeren.Som du kan se, fungerer ikke en funksjon vanligvis komme tilbake en NSError
objekt-det returnerer hvilken verdi den skal hvis den lykkes, ellers returnerer den nil
. Du bør alltid bruke returverdien til en funksjon for å oppdage feil. Bruk aldri tilstedeværelsen eller fraværet av en NSError
Motta for å se om en handling lykkes. Feilobjekter skal bare beskrive en potensiell feil, ikke fortelle om det oppstod en.
Følgende eksempel viser en realistisk brukstilstand for NSError
. Den bruker en fil-lasting metode av NSString
, som faktisk ligger utenfor bokens omfang. De iOS Succinctly Boken dekker filbehandling i dybden, men for nå, la oss bare fokusere på feilhåndteringsfunksjonene til Objective-C.
Først genererer vi en filsti som peker på ~ / Desktop / SomeContent.txt.
Deretter oppretter vi en NSError
referanse og send det til stringWithContentsOfFile: koding: error:
metode for å fange opp informasjon om eventuelle feil som oppstår under lasting av filen. Legg merke til at vi passerer en henvisning til *feil
pekeren, som betyr at metoden er å be om en peker til en peker (dvs. en dobbeltpeker). Dette gjør det mulig for metoden å fylle variabelen med eget innhold. Til slutt, vi sjekker returverdi (ikke eksistensen av feil
variabel) for å se om stringWithContentsOfFile: koding: error:
lykkes eller ikke. Hvis det gjorde det, er det trygt å arbeide med verdien som er lagret i innhold
variabel; ellers bruker vi feil
variabel for å vise informasjon om hva som gikk galt.
#importereint main (int argc, const char * argv []) @ autoreleasepool // Generer den ønskede filbanen. NSString * filnavn = @ "SomeContent.txt"; NSArray * stier = NSSearchPathForDirectoriesInDomains (NSDesktopDirectory, NSUserDomainMask, YES); NSString * desktopDir = [stier objectAtIndex: 0]; NSString * bane = [desktopDir stringByAppendingPathComponent: filnavn]; // Prøv å laste filen. NSError * feil; NSString * content = [NSString stringWithContentsOfFile: stikoding: NSUTF8StringEncoding error: & error]; // Sjekk om det virket. hvis (innhold == nil) // Noen feil oppstod. NSLog (@ "Feil ved lasting av fil% @!", Bane); NSLog (@ "Description:% @", [error localizedDescription]); NSLog (@ "Årsak:% @", [feil localizedFailureReason]); ellers // innhold lastet vellykket. NSLog (@ "Innhold lastet!"); NSLog (@ "% @", innhold); returnere 0;
Siden ~ / Desktop / SomeContent.txt
filen finnes sannsynligvis ikke på maskinen din, vil denne koden sannsynligvis føre til en feil. Alt du trenger å gjøre for å få lasten til å lykkes er å skape SomeContent.txt
på skrivebordet ditt.
Egendefinerte feil kan konfigureres ved å akseptere en dobbeltpeker til en NSError
objekt og befolke det på egen hånd. Husk at din funksjon eller metode skal returnere enten et objekt eller nil
, avhengig av om det lykkes eller feiler (ikke returner NSError
henvisning).
Det neste eksemplet bruker en feil i stedet for et unntak for å redusere ugyldige parametere i generateRandomInteger ()
funksjon. Legg merke til det **feil
er en dobbel pointer, som lar oss fylle den underliggende variabelen fra funksjonen. Det er veldig viktig å kontrollere at brukeren faktisk har bestått en gyldig **feil
parameter med hvis (feil! = NULL)
. Du bør alltid gjøre dette i dine egne feilgenererende funksjoner. Siden **feil
parameteren er en dobbel pointer, vi kan tilordne en verdi til den underliggende variabelen via *feil
. Og igjen, vi ser etter feil ved bruk av returverdi (hvis (resultat == nil)
), ikke feil
variabel.
#importereNSNumber * generateRandomInteger (int minimum, int maksimum, NSError ** feil) hvis (minimum> = maksimum) if (error! = NULL) // Opprett feilen. NSString * domain = @ "com.MyCompany.RandomProject.ErrorDomain"; int errorCode = 4; NSMutableDictionary * userInfo = [NSMutableDictionary ordbok]; [userInfo setObject: @ "Maksimum parameter er ikke større enn minimum parameter" forKey: NSLocalizedDescriptionKey]; // Populere feilreferansen. * error = [[NSError alloc] initWithDomain: domenekode: errorCode userInfo: userInfo]; returner null; // Returner et tilfeldig heltall. returnere [NSNummer nummerWithInt: arc4random_uniform ((maksimum - minimum) + 1) + minimum]; int main (int argc, const char * argv []) @ autoreleasepool NSError * feil; NSNumber * result = generateRandomInteger (0, -10, & error); hvis (resultat == nil) // Sjekk for å se hva som gikk galt. NSLog (@ "En feil oppstod!"); NSLog (@ "Domain:% @ Code:% li", [feil-domene], [feilkode]); NSLog (@ "Description:% @", [error localizedDescription]); else // Trygg å bruke returverdien. NSLog (@ "Tilfeldig nummer:% i", [result intValue]); returnere 0;
Alle localizedDescription
, localizedFailureReason
, og beslektede egenskaper av NSError
er faktisk lagret i sin brukerinformasjon
ordbok ved hjelp av spesielle taster definert av NSLocalizedDescriptionKey
, NSLocalizedFailureReasonErrorKey
, etc. Så alt vi trenger å gjøre for å beskrive feilen, er å legge til noen strenger til de riktige tastene, som vist i den siste prøven.
Vanligvis vil du definere konstanter for egendefinerte feildomener og koder slik at de er konsistente på tvers av klasser.
Dette kapitlet ga en detaljert diskusjon om forskjellene mellom unntak og feil. Unntak er utformet for å informere programmører om dødelige problemer i deres program, mens feil representerer en mislykket brukerhandling. Generelt bør en produksjonsklar applikasjon ikke kaste unntak, unntatt i tilfelle virkelig uvanlige omstendigheter (for eksempel å gå tom for minne i en enhet).
Vi dekket den grunnleggende bruken av NSError
, men husk at det er flere innebygde klasser dedikert til å behandle og vise feil. Dessverre er disse alle grafiske komponenter, og dermed utenfor omfanget av denne boken. De iOS Succinctly oppfølger har en dedikert seksjon om å vise og gjenopprette fra feil.
I det siste kapitlet i Mål-C Succinctly, Vi diskuterer et av de mer forvirrende emnene i Objective-C. Vi vil finne ut hvordan blokker la oss behandle funksjonalitet På samme måte som vi behandler data. Dette vil ha en vidtgående innvirkning på hva som er mulig i et mål-C-program.
Denne leksjonen representerer et kapittel fra Objective-C Succinctly, en gratis eBok fra teamet på Syncfusion.