In Objective-C zijn er twee soorten fouten die kunnen optreden terwijl een programma wordt uitgevoerd. Niet verwacht fouten zijn "ernstige" programmeerfouten die ervoor zorgen dat uw programma voortijdig wordt afgesloten. Deze worden genoemd uitzonderingen, omdat ze een uitzonderlijke conditie vertegenwoordigen in je programma. Anderzijds, verwacht fouten komen van nature voor in de loop van de uitvoering van een programma en kunnen worden gebruikt om het succes van een operatie te bepalen. Deze worden de fouten.
U kunt ook het onderscheid tussen uitzonderingen en fouten benaderen als een verschil in hun doelgroepen. In het algemeen worden uitzonderingen gebruikt om de programmeur over iets dat fout is gegaan, terwijl fouten worden gebruikt om de gebruiker dat een aangevraagde actie niet kon worden voltooid.
Controlestroom voor uitzonderingen en foutenEen poging om toegang te krijgen tot een arrayindex die niet bestaat, is bijvoorbeeld een uitzondering (een programmorfout), terwijl het niet openen van een bestand een fout is (een gebruikersfout). In het eerste geval ging er iets heel erg mis in de stroom van uw programma en zou het waarschijnlijk snel moeten sluiten na de uitzondering. In het laatste geval zou je de gebruiker willen vertellen dat het bestand niet kan worden geopend en mogelijk vragen om de actie opnieuw te proberen, maar er is geen reden waarom je programma na de fout niet zou kunnen blijven werken.
Het belangrijkste voordeel van de exception handling-mogelijkheden van Objective-C is de mogelijkheid om de afhandeling van fouten van de detectie van fouten te scheiden. Wanneer een deel van de code een uitzondering tegenkomt, kan deze deze "weggooien" naar het dichtstbijzijnde foutafhandelingsblok, dat specifieke uitzonderingen kan "vangen" en deze op de juiste manier behandelt. Het feit dat uitzonderingen kunnen worden gegooid vanaf willekeurige locaties elimineert de noodzaak om voortdurend te controleren op succes- of faalmeldingen van elke functie die bij een bepaalde taak betrokken is.
De @proberen
, @vangst()
, en @Tenslotte
compiler-richtlijnen worden gebruikt om uitzonderingen op te vangen en te behandelen, en de @gooien
richtlijn wordt gebruikt om ze te detecteren. Als je in C # met uitzonderingen hebt gewerkt, zouden deze constructies voor het afhandelen van uitzonderingen je bekend moeten zijn.
Het is belangrijk op te merken dat in Objective-C uitzonderingen relatief traag zijn. Dientengevolge, zou hun gebruik moeten worden beperkt tot het vangen van serieuze programmeerfouten - niet voor elementaire besturingsstroom. Als u probeert te bepalen wat u moet doen op basis van een verwacht fout (bijv. het niet laden van een bestand), raadpleeg de Foutafhandeling sectie.
Uitzonderingen worden weergegeven als exemplaren van de NSException
klasse of een subklasse daarvan. Dit is een handige manier om alle benodigde informatie in verband met een uitzondering in te kapselen. De drie eigenschappen die een uitzondering vormen, worden als volgt beschreven:
naam
- Een instantie van NSString
die op unieke wijze de uitzondering identificeert.reden
- Een instantie van NSString
met een voor mensen leesbare beschrijving van de uitzondering.gebruikers informatie
- Een instantie van NSDictionary
die toepassingsspecifieke informatie bevat met betrekking tot de uitzondering.Het Foundation-raamwerk definieert verschillende constanten die de "standaard" uitzonderingsnamen definiëren. Deze reeksen kunnen worden gebruikt om te controleren welk type uitzonderingen zijn betrapt.
U kunt ook de initWithName: reden: userinfo:
initialisatiemethode om nieuwe uitzonderingsobjecten met uw eigen waarden te maken. Aangepaste uitzonderingsobjecten kunnen worden gevangen en gegooid met behulp van dezelfde methoden die worden behandeld in de komende secties.
Laten we beginnen met een kijkje te nemen naar het standaard-uitzonderingsafhandelingsgedrag van een programma. De objectAtIndex:
methode van NSArray
is gedefinieerd om een te gooien NSRangeException
(een subklasse van NSException
) wanneer u een index probeert te openen die niet bestaat. Dus als u de 10 aanvraagtth item van een array met slechts drie elementen, dan heb je een uitzondering om mee te experimenteren:
#importerenint main (int argc, const char * argv []) @autoreleasepool NSArray * crew = [NSArray arrayWithObjects: @ "Dave", @ "Heywood", @ "Frank", nihil]; // Dit zal een uitzondering opleveren. NSLog (@ "% @", [crew objectAtIndex: 10]); retourneer 0;
Wanneer het een niet-opgevangen uitzondering tegenkomt, stopt Xcode het programma en wijst het naar de regel die het probleem veroorzaakte.
Een programma afbreken vanwege een niet-afgevangen uitzonderingVervolgens leren we uitzonderingen vast te leggen en te voorkomen dat het programma wordt beëindigd.
Om een uitzondering af te handelen, elke code die mei resultaat in een uitzondering moet worden geplaatst in een @proberen
blok. Vervolgens kunt u specifieke uitzonderingen vastleggen met behulp van de @vangst()
richtlijn. Als u een huishoudcode wilt uitvoeren, kunt u deze optioneel in een @Tenslotte
blok. Het volgende voorbeeld toont alle drie van deze richtlijnen voor het afhandelen van uitzonderingen:
@try NSLog (@ "% @", [crew objectAtIndex: 10]); @catch (uitzondering NSException *) NSLog (@ "Caught a exception"); // We negeren de uitzondering gewoon stil. @ definitief NSLog (@ "Opschonen");
Dit zou het volgende moeten uitvoeren in uw Xcode-console:
Gevangen een uitzondering! Naam: NSRangeException Reden: *** - [__ NSArrayI objectAtIndex:]: index 10 beyond bounds [0 ... 2] Opschonen
Wanneer het programma de [crew objectAtIndex: 10]
bericht, het gooit een NSRangeException
, die is gevangen in de @vangst()
richtlijn. Binnenkant van de @vangst()
blok is waar de uitzondering feitelijk wordt afgehandeld. In dit geval geven we alleen een beschrijvend foutbericht weer, maar in de meeste gevallen wilt u waarschijnlijk een code schrijven om het probleem op te lossen.
Wanneer een uitzondering wordt aangetroffen in de @proberen
blok, springt het programma naar het overeenkomstige @vangst()
blok, wat elke code betekent na de opgetreden uitzondering zal niet worden uitgevoerd. Dit vormt een probleem als het @proberen
blok moet worden opgeschoond (bijvoorbeeld als een bestand is geopend, moet dat bestand worden gesloten). De @Tenslotte
block lost dit probleem op, omdat het dat wel is gegarandeerd om te worden uitgevoerd, ongeacht of er een uitzondering is opgetreden. Dit maakt het de perfecte plek om losse eindjes van de @proberen
blok.
De haakjes na de @vangst()
richtlijn laat u definiëren welk type uitzondering u probeert te vangen. In dit geval is het een NSException
, wat de standaard uitzonderingsklasse is. Maar een uitzondering kan eigenlijk zijn ieder klasse - niet alleen een NSException
. Bijvoorbeeld het volgende @vangst()
richtlijn zal een generiek object behandelen:
@catch (id genericException)
We zullen voorbeelden leren van instanties NSException
evenals generieke objecten in de volgende sectie.
Wanneer u een uitzonderlijke voorwaarde in uw code aantreft, maakt u een instantie van NSException
en vul het met de relevante informatie. Vervolgens gooi je het met de toepasselijke naam @gooien
richtlijn, waarbij de dichtstbijzijnde wordt opgeroepen @proberen
/@vangst
blokkeren om het aan te pakken.
In het volgende voorbeeld wordt bijvoorbeeld een functie gedefinieerd voor het genereren van willekeurige getallen tussen een opgegeven interval. Als de beller een ongeldig interval doorgeeft, genereert de functie een aangepaste fout.
#importerenint generateRandomInteger (int minimum, int maximum) if (minimum> = maximum) // Maak de uitzondering. NSException * exception = [NSException exceptionWithName: @ "RandomNumberIntervalException" reden: @ "*** generateRandomInteger ():" "maximum parameter niet groter dan minimum parameter" userInfo: nil]; // Gooi de uitzondering. @ Throw-uitzondering; // Retourneer een willekeurig geheel getal. return arc4random_uniform ((maximum - minimum) + 1) + minimum; int main (int argc, const char * argv []) @autoreleasepool int result = 0; @try result = generateRandomInteger (0, 10); @catch (uitzondering NSException *) NSLog (@ "Probleem !!! Gevangen uitzondering:% @", [naam uitzondering]); NSLog (@ "Willekeurig getal:% i", resultaat); retourneer 0;
Aangezien deze code een geldig interval doorgeeft (0, 10
) naar generateRandomInteger ()
, het zal geen uitzondering hebben om te vangen. Als u het interval echter verandert in iets als (0, -10
), krijg je de @vangst()
blokkeren in actie. Dit is in essentie wat er gebeurt onder de motorkap wanneer de framework-klassen uitzonderingen tegenkomen (bijv NSRangeException
opgevoed door NSArray
).
Het is ook mogelijk om uitzonderingen die je al hebt gevangen opnieuw te gooien. Dit is handig als u op de hoogte wilt worden gehouden dat er een bepaalde uitzondering is opgetreden, maar deze niet noodzakelijkerwijs zelf wilt afhandelen. Voor het gemak kunt u het argument zelfs weglaten naar de @gooien
richtlijn:
@try result = generateRandomInteger (0, -10); @catch (uitzondering NSException *) NSLog (@ "Probleem !!! Gevangen uitzondering:% @", [naam uitzondering]); // Werp de huidige uitzondering opnieuw. @gooien
Hiermee wordt de opgevangen uitzondering doorgegeven aan de eerstvolgende hoogste handler, wat in dit geval de uitzonderingshandler op het hoogste niveau is. Dit zou de uitvoer van onze moeten weergeven @vangst()
blokkeren, evenals de standaardwaarde App beëindigen als gevolg van niet-afgevangen uitzondering ...
bericht, gevolgd door een abrupte exit.
De @gooien
richtlijn is niet beperkt tot NSException
objecten - het kan letterlijk gooien ieder voorwerp. Het volgende voorbeeld gooit een NSNumber
object in plaats van een normale uitzondering. Merk ook op hoe u verschillende objecten kunt targeten door er meerdere toe te voegen @vangst()
verklaringen na de @proberen
blok:
#importerenint generateRandomInteger (int minimum, int maximum) if (minimum> = maximum) // Genereer een getal met het interval "default". NSNumber * guess = [NSNumber numberWithInt: generateRandomInteger (0, 10)]; // Gooi het nummer. @throw guess; // Retourneer een willekeurig geheel getal. return arc4random_uniform ((maximum - minimum) + 1) + minimum; int main (int argc, const char * argv []) @autoreleasepool int result = 0; @try result = generateRandomInteger (30, 10); @catch (NSNumber * guess) NSLog (@ "Warning: Used default interval"); result = [guess intValue]; @catch (uitzondering NSException *) NSLog (@ "Probleem !!! Gevangen uitzondering:% @", [naam uitzondering]); NSLog (@ "Willekeurig getal:% i", resultaat); retourneer 0;
In plaats van een NSException
voorwerp, generateRandomInteger ()
probeert een nieuw nummer te genereren tussen sommige "standaard" grenzen. Het voorbeeld laat zien hoe @gooien
kan werken met verschillende soorten objecten, maar strikt genomen is dit niet het beste ontwerp van de toepassing, noch is het het meest efficiënte gebruik van de exception-handling-tools van Objective-C. Als je echt net van plan was om de gegooide waarde te gebruiken zoals de vorige code doet, zou je beter af zijn met een eenvoudige oude voorwaardelijke cheque met NSError
, zoals besproken in de volgende sectie.
Daarnaast zijn enkele kernkaders van Apple verwachten een NSException
object dat moet worden gegooid, dus wees voorzichtig met aangepaste objecten bij het integreren met de standaardbibliotheken.
Terwijl uitzonderingen zijn ontworpen om programmeurs te laten weten wanneer dingen verkeerd zijn gegaan, worden fouten zodanig ontworpen dat ze een efficiënte, eenvoudige manier zijn om te controleren of een actie slaagt of niet. In tegenstelling tot uitzonderingen, fouten zijn ontworpen om te worden gebruikt in de stroomverklaringen van uw dagelijkse controle.
Het enige dat fouten en uitzonderingen gemeen hebben, is dat ze allebei als objecten worden geïmplementeerd. De NSError
class bevat alle benodigde informatie voor het weergeven van fouten:
code
- Een NSInteger
dat staat voor de unieke identifier van de fout.domein
- Een instantie van NSString
het definiëren van het domein voor de fout (meer in detail beschreven in de volgende sectie).gebruikers informatie
- Een instantie van NSDictionary
die toepassingsspecifieke informatie bevat die gerelateerd is aan de fout. Dit wordt meestal veel meer gebruikt dan de gebruikers informatie
woordenboek van NSException
.Naast deze kernattributen, NSError
slaat ook verschillende waarden op die zijn ontworpen om te helpen bij het weergeven en verwerken van fouten. Al deze zijn eigenlijk snelkoppelingen naar de gebruikers informatie
woordenboek beschreven in de vorige lijst.
localizedDescription
- Een NSString
die de volledige beschrijving van de fout bevat, die meestal de reden voor de fout bevat. Deze waarde wordt meestal weergegeven aan de gebruiker in een waarschuwingsvenster.localizedFailureReason
- Een NSString
met een zelfstandige beschrijving van de reden voor de fout. Dit wordt alleen gebruikt door klanten die de reden voor de fout willen isoleren uit de volledige beschrijving.recoverySuggestion
- Een NSString
de gebruiker instrueren hoe hij van de fout kan herstellen.localizedRecoveryOptions
- Een NSArray
van titels die worden gebruikt voor de knoppen in het foutdialoogvenster. Als deze array leeg is, een single OK knop wordt weergegeven om de waarschuwing te negeren.helpAnchor
- Een NSString
om weer te geven wanneer de gebruiker op de Helpen ankerknop in een waarschuwingspaneel.Zoals met NSException
, de initWithDomain: code: userinfo
methode kan worden gebruikt om custom te initialiseren NSError
instanties.
Een foutendomein is als een naamruimte voor foutcodes. Codes moeten binnen een enkel domein uniek zijn, maar ze kunnen overlappen met codes uit andere domeinen. Naast het voorkomen van codebotsingen, bieden domeinen ook informatie over waar de fout vandaan komt. De vier belangrijkste ingebouwde foutdomeinen zijn: NSMachErrorDomain
, NSPOSIXErrorDomain
, NSOSStatusErrorDomain
, en NSCocoaErrorDomain
. De NSCocoaErrorDomain
bevat de foutcodes voor veel van de standaard Objective-C-raamwerken van Apple; er zijn echter enkele raamwerken die hun eigen domeinen definiëren (bijv., NSXMLParserErrorDomain
).
Als u aangepaste foutcodes voor uw bibliotheken en toepassingen moet maken, moet u deze altijd toevoegen je eigen foutdomein - breid nooit een van de ingebouwde domeinen uit. Het maken van uw eigen domein is een relatief triviale taak. Omdat domeinen slechts strings zijn, hoeft u alleen maar een reeksconstante te definiëren die niet conflicteert met een van de andere foutdomeinen in uw toepassing. Apple suggereert dat domeinen de vorm aannemen van com.
.
Er zijn geen speciale taalconstructies voor afhandeling NSError
instanties (hoewel verschillende ingebouwde klassen zijn ontworpen om ze te verwerken). Ze zijn ontworpen om te worden gebruikt in combinatie met speciaal ontworpen functies die een object retourneren wanneer ze slagen en nul
wanneer ze falen. De algemene procedure voor het vastleggen van fouten is als volgt:
NSError
variabel. U hoeft het niet toe te wijzen of te initialiseren.NSError
om de fout zelf af te handelen of aan de gebruiker te tonen.Zoals u kunt zien, is een functie niet typisch terugkeer een NSError
object-it retourneert elke waarde die het zou moeten hebben als het lukt, anders komt het terug nul
. Gebruik altijd de retourwaarde van een functie om fouten te detecteren - gebruik nooit de aanwezigheid of afwezigheid van een NSError
object om te controleren of een actie slaagde. Foutobjecten zijn alleen bedoeld om een potentiële fout te beschrijven, niet om te vertellen of er een is opgetreden.
Het volgende voorbeeld laat een realistische gebruikscasus zien voor NSError
. Het gebruikt een methode voor het laden van bestanden van NSString
, wat eigenlijk buiten de scope van het boek valt. De iOS bondig boekomslagen omvatten bestandsbeheer diepgaand, maar laten we ons nu concentreren op de foutafhandelingsmogelijkheden van Objective-C.
Eerst genereren we een pad naar ~ / Desktop / SomeContent.txt.
Vervolgens maken we een NSError
verwijzing en geef deze door aan de stringWithContentsOfFile: encoding: error:
methode om informatie te verzamelen over eventuele fouten die optreden tijdens het laden van het bestand. Merk op dat we een a passeren referentie naar de *fout
pointer, wat betekent dat de methode een aanwijzer vraagt naar een aanwijzer (dat wil zeggen een dubbele aanwijzer). Dit maakt het mogelijk voor de methode om de variabele met zijn eigen inhoud te vullen. Eindelijk controleren we de winstwaarde (niet het bestaan van de fout
variabele) om te zien of stringWithContentsOfFile: encoding: error:
geslaagd of niet. Als dat zo is, is het veilig om te werken met de waarde die is opgeslagen in de inhoud
variable; anders gebruiken we de fout
variabele om informatie weer te geven over wat er mis ging.
#importerenint main (int argc, const char * argv []) @autoreleasepool // Genereer het gewenste bestandspad. NSString * bestandsnaam = @ "SomeContent.txt"; NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDesktopDirectory, NSUserDomainMask, YES); NSString * desktopDir = [paths objectAtIndex: 0]; NSString * path = [desktopDir stringByAppendingPathComponent: bestandsnaam]; // Probeer het bestand te laden. NSError * -fout; NSString * content = [NSString stringWithContentsOfFile: padcodering: NSUTF8StringEncoding-fout: & fout]; // Controleer of het werkte. if (content == nil) // Er is een fout opgetreden. NSLog (@ "Fout bij laden van bestand% @!", Pad); NSLog (@ "Description:% @", [error localizedDescription]); NSLog (@ "Reason:% @", [error localizedFailureReason]); else // Inhoud is geladen. NSLog (@ "Inhoud geladen!"); NSLog (@ "% @", inhoud); retourneert 0;
Sinds de ~ / Desktop / SomeContent.txt
bestand bestaat waarschijnlijk niet op uw computer, deze code zal hoogstwaarschijnlijk in een fout resulteren. Alles wat u hoeft te doen om de taak te laten slagen, is maken SomeContent.txt
op je bureaublad.
Aangepaste fouten kunnen worden geconfigureerd door een dubbele aanwijzer te accepteren als een NSError
object en vul het zelf. Onthoud dat uw functie of methode een object of nul
, afhankelijk van of het lukt of mislukt (niet retourneren NSError
referentie).
Het volgende voorbeeld gebruikt een fout in plaats van een uitzondering om ongeldige parameters in de generateRandomInteger ()
functie. Let erop dat **fout
is een dubbele aanwijzer, waarmee we de onderliggende variabele vanuit de functie kunnen vullen. Het is erg belangrijk om te controleren of de gebruiker daadwerkelijk een geldige heeft doorgegeven **fout
parameter met if (error! = NULL)
. U moet dit altijd doen in uw eigen fouten genererende functies. Sinds de **fout
parameter is een dubbele aanwijzer, we kunnen via een waarde aan de onderliggende variabele toewijzen *fout
. En nogmaals, we controleren op fouten met behulp van de winstwaarde (if (result == nihil)
), niet de fout
veranderlijk.
#importerenNSNumber * generateRandomInteger (int minimum, int maximum, NSError ** error) if (minimum> = maximum) if (error! = NULL) // Maak de fout. NSString * domein = @ "com.MyCompany.RandomProject.ErrorDomain"; int errorCode = 4; NSMutableDictionary * userInfo = [NSMutableDictionary dictionary]; [userInfo setObject: @ "Maximale parameter is niet groter dan minimale parameter" forKey: NSLocalizedDescriptionSleutel]; // Vul de foutreferentie in. * fout = [[NSError alloc] initWithDomain: domeincode: errorCode userInfo: userInfo]; terug nul; // Retourneer een willekeurig geheel getal. return [NSNumber numberWithInt: arc4random_uniform ((maximum - minimum) + 1) + minimum]; int main (int argc, const char * argv []) @autoreleasepool NSError * error; NSNumber * result = generateRandomInteger (0, -10, & error); if (result == nil) // Controleer wat er mis is gegaan. NSLog (@ "Er is een fout opgetreden!"); NSLog (@ "Domein:% @ Code:% li", [foutdomein], [foutcode]); NSLog (@ "Description:% @", [error localizedDescription]); else // Veilig om de geretourneerde waarde te gebruiken. NSLog (@ "Random Number:% i", [result intValue]); retourneert 0;
Alle localizedDescription
, localizedFailureReason
, en gerelateerde eigenschappen van NSError
zijn eigenlijk opgeslagen in zijn gebruikers informatie
woordenboek met speciale sleutels gedefinieerd door NSLocalizedDescriptionKey
, NSLocalizedFailureReasonErrorKey
, enz. Dus, alles wat we moeten doen om de fout te beschrijven, is om een aantal reeksen toe te voegen aan de juiste toetsen, zoals te zien in het laatste voorbeeld.
Meestal wilt u constanten definiëren voor aangepaste foutdomeinen en codes, zodat deze consistent zijn in alle klassen.
Dit hoofdstuk bevat een gedetailleerde bespreking van de verschillen tussen uitzonderingen en fouten. Uitzonderingen zijn bedoeld om programmeurs te informeren over fatale problemen in hun programma, terwijl fouten een mislukte gebruikersactie vertegenwoordigen. Over het algemeen moet een productieafhankelijke toepassing dit doen niet uitzonderingen, behalve in het geval van werkelijk uitzonderlijke omstandigheden (bijvoorbeeld onvoldoende geheugen in een apparaat).
We hebben het basisgebruik van NSError
, maar houd er rekening mee dat er verschillende ingebouwde klassen zijn speciaal voor het verwerken en weergeven van fouten. Helaas zijn dit allemaal grafische componenten en dus buiten het bestek van dit boek. De iOS bondig vervolg heeft een speciale sectie over het weergeven en herstellen van fouten.
In het laatste hoofdstuk van Objectief-C bondig, we bespreken een van de meer verwarrende onderwerpen in Objective-C. We zullen ontdekken hoe blokken ons behandelen functionaliteit op dezelfde manier als wij behandelen gegevens. Dit zal een verreikende invloed hebben op wat mogelijk is in een Objective-C-toepassing.
Deze les vertegenwoordigt een hoofdstuk uit Objective-C bondig, een gratis eBoek van het team van Syncfusion.