gettando un'eccezione in obiettivo-c / cacao


Risposte:


528

Io uso [NSException raise:format:]come segue:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];

9
Preferisco così l' @throw([NSException exceptionWith…])approccio all'approccio in quanto più conciso.
Sam Soffes,

9
Assicurarsi di leggere l'avvertenza importante da Harms ( stackoverflow.com/questions/324284/324805#324805 )
e.James

26
In genere preferisco anche questo, ma c'è un gotcha. Potrebbe essere solo la mia versione attuale di Xcode, ma la sintassi [aumento NSException ...] non sembra essere riconosciuta dal parser come un percorso di uscita da un metodo che restituisce un valore. Ricevo l'avvertimento "Il controllo può raggiungere la fine della funzione non nulla" quando si utilizza questa sintassi, ma con la sintassi @throw ([NSException eccezione con ...]), il parser riconosce che come uscita e non visualizza l'avviso.
Mattorb,

1
@mpstx Uso sempre la sintassi di lancio per il motivo che hai indicato (che è ancora rilevante due anni dopo in Xcode 4.6, e probabilmente lo sarà sempre). Far riconoscere l'IDE che il lancio di un'eccezione è un punto di uscita della funzione è spesso importante se si desidera evitare avvisi.
Mark Amery,

FWIW Sto notando che i blocchi @ try / @ catch generano anche un falso negativo per gli avvisi "il controllo raggiunge la fine della funzione non nulla" (ovvero l'avviso non viene visualizzato quando dovrebbe essere)
Brian Gerstle

256

Una parola di cautela qui. In Objective-C, a differenza di molti linguaggi simili, in genere si dovrebbe cercare di evitare l'uso di eccezioni per situazioni di errore comuni che possono verificarsi durante il normale funzionamento.

La documentazione di Apple per Obj-C 2.0 afferma quanto segue: "Importante: le eccezioni fanno un uso intensivo delle risorse in Objective-C. Non dovresti usare eccezioni per il controllo generale del flusso o semplicemente per indicare errori (come un file non accessibile)"

La documentazione concettuale sulla gestione delle eccezioni di Apple spiega lo stesso, ma con più parole: "Importante: dovresti riservare l'uso di eccezioni per errori di runtime di programmazione o imprevisti come l'accesso alla raccolta fuori limite, tentativi di mutare oggetti immutabili, invio di un messaggio non valido e la perdita della connessione al server di finestre. In genere si prendono cura di questo tipo di errori con eccezioni quando viene creata un'applicazione anziché in fase di esecuzione. [.....] Invece di eccezioni, oggetti errore (NSError) e Il meccanismo di consegna degli errori di cacao è il modo raccomandato per comunicare gli errori previsti nelle applicazioni di cacao. "

Le ragioni di ciò sono in parte aderire ai linguaggi di programmazione in Objective-C (utilizzando valori di ritorno in casi semplici e parametri di riferimento (spesso la classe NSError) in casi più complessi), in parte il fatto che lanciare e catturare le eccezioni è molto più costoso e infine (e per lo più importante) che le eccezioni Objective-C sono un sottile wrapper attorno alle funzioni setjmp () e longjmp () di C, che in sostanza incasinano la tua attenta gestione della memoria, vedi questa spiegazione .


11
Penso che questo si applichi alla maggior parte dei linguaggi di programmazione: "cerca di evitare di usare eccezioni per situazioni di errore comuni". Lo stesso vale in Java; è una buona pratica gestire gli errori di input dell'utente (ad esempio) con eccezioni. Non solo per l'utilizzo delle risorse, ma anche per chiarezza del codice.
Beetstra,

6
Ancora più importante, le eccezioni in Cocoa sono progettate per indicare errori di programma non recuperabili. Fare diversamente è in esecuzione contro il framework e può portare a comportamenti indefiniti. Per ulteriori dettagli, consultare stackoverflow.com/questions/3378696/iphone-try-end-try/… .
KPM,

9
"Lo stesso vale in Java;" Disaccordo. È possibile utilizzare le eccezioni verificate in Java bene per normali condizioni di errore. Ovviamente non useresti le eccezioni di Runtime.
Daniel Ryan,

Preferisco il modo Cocoa (le eccezioni sono solo per gli errori del programmatore) quindi preferirei farlo anche in Java, ma la realtà è che dovresti seguire le pratiche tipiche in un ambiente e le eccezioni per la gestione degli errori spiccano come un cattivo odore in Objective-C, ma sono usati molto a tale scopo in Java.
gnasher729,

1
Questo commento non risponde alla domanda. Forse l'OP vuole semplicemente arrestare l'app per verificare se il framework di report sugli arresti anomali funziona come previsto.
Simon,

62
@throw([NSException exceptionWith…])

Xcode riconosce le @throwistruzioni come punti di uscita delle funzioni, come le returnistruzioni. L'uso della @throwsintassi evita errori errati "Il controllo può raggiungere la fine della funzione non nulla " che potresti ricevere [NSException raise:…].

Inoltre, @throwpuò essere utilizzato per lanciare oggetti che non appartengono alla classe NSException.


11
@Steph Thirion: vedi developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/… per tutti i dettagli. Linea di fondo? Entrambi funzioneranno, ma @throw può essere usato per lanciare oggetti che non sono della classe NSException.
e.James,

33

Per quanto riguarda [NSException raise:format:]. Per coloro che provengono da uno sfondo Java, ricorderete che Java distingue tra Exception e RuntimeException. L'eccezione è un'eccezione verificata e RuntimeException non è selezionata. In particolare, Java suggerisce di utilizzare le eccezioni verificate per "condizioni di errore normali" e le eccezioni non selezionate per "errori di runtime causati da un errore del programmatore". Sembra che le eccezioni Objective-C debbano essere utilizzate negli stessi punti in cui si utilizzerebbe un'eccezione non selezionata, mentre i valori di ritorno del codice di errore o i valori NSError sono preferiti nei luoghi in cui si utilizzerà un'eccezione controllata.


1
Sì, è corretto (dopo 4 anni: D), crea la tua classe di errore ABCError che si estende dalla classe NSError e usala per le eccezioni verificate anziché per NSExceptions. Solleva NSExceptions in cui si verificano errori del programmatore (situazione imprevista come un problema di formato numerico).
Chathuram,

15

Penso che sia coerente, è meglio usare @throw con la tua classe che estende NSException. Quindi usi le stesse notazioni per provare a catturare finalmente:

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple spiega qui come generare e gestire le eccezioni: Cattura delle eccezioni Lancio delle eccezioni


ho ancora avuto un crash per eccezione di runtime nel blocco try
famfamfam

14

A partire da ObjC 2.0, le eccezioni Objective-C non sono più un wrapper per setjmp () longjmp () di C e sono compatibili con l'eccezione C ++, @try è "gratuito", ma lanciare e catturare le eccezioni è molto più costoso.

Ad ogni modo, le asserzioni (usando la famiglia di macro NSAssert e NSCAssert) generano NSException, e questo è ragionevole usarle come stati di Ries.


Buono a sapersi! Abbiamo una libreria di terze parti che non vogliamo modificare che genera eccezioni anche per gli errori più piccoli. Dobbiamo prenderli in un posto nell'app e questo ci fa solo rabbrividire, ma questo mi fa sentire un po 'meglio.
Yuri Brigance,

8

Utilizzare NSError per comunicare errori anziché eccezioni.

Punti rapidi su NSError:

  • NSError consente ai codici di errore in stile C (numeri interi) di identificare chiaramente la causa principale e, si spera, consentire al gestore degli errori di superare l'errore. Puoi avvolgere i codici di errore da librerie C come SQLite nelle istanze di NSError molto facilmente.

  • NSError ha anche il vantaggio di essere un oggetto e offre un modo per descrivere l'errore in modo più dettagliato con il suo membro del dizionario userInfo.

  • Ma soprattutto, NSError NON PUO 'essere lanciato in modo da incoraggiare un approccio più proattivo alla gestione degli errori, al contrario di altre lingue che semplicemente spingono ulteriormente la patata bollente e aumentano ulteriormente lo stack di chiamate a quel punto può essere segnalato solo all'utente e non gestito in alcun modo significativo (non se credi nel seguire il più grande principio di OOP che nasconde le informazioni).

Link di riferimento : riferimento


Questo commento non risponde alla domanda. Forse l'OP vuole semplicemente arrestare l'app per verificare se il framework di report sugli arresti anomali funziona come previsto.
Simon,

7

È così che l'ho imparato da "The Big Nerd Ranch Guide (4th edition)":

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];

OK, ma non dice molto sul userInfo:nil. :)
Cœur il

6

È possibile utilizzare due metodi per sollevare un'eccezione nel blocco try catch

@throw[NSException exceptionWithName];

o il secondo metodo

NSException e;
[e raise];

3

Credo che non dovresti mai usare Eccezioni per controllare il normale flusso del programma. Ma dovrebbero essere generate eccezioni ogni volta che un valore non corrisponde a un valore desiderato.

Ad esempio, se una funzione accetta un valore e quel valore non è mai ammesso che sia zero, allora va bene lanciare un'eccezione piuttosto che provare a fare qualcosa di "intelligente" ...

Ries


0

Dovresti generare eccezioni solo se ti trovi in ​​una situazione che indica un errore di programmazione e vuoi interrompere l'esecuzione dell'applicazione. Pertanto, il modo migliore per generare eccezioni è utilizzare le macro NSAssert e NSParameterAssert e assicurarsi che NS_BLOCK_ASSERTIONS non sia definito.


0

Codice di esempio per case: @throw ([NSException exceptionWithName: ...

- (void)parseError:(NSError *)error
       completionBlock:(void (^)(NSString *error))completionBlock {


    NSString *resultString = [NSString new];

    @try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    resultString = dictFromData[@"someKey"];
    ...


} @catch (NSException *exception) {

      NSLog( @"Caught Exception Name: %@", exception.name);
      NSLog( @"Caught Exception Reason: %@", exception.reason );

    resultString = exception.reason;

} @finally {

    completionBlock(resultString);
}

}

usando:

[self parseError:error completionBlock:^(NSString *error) {
            NSLog(@"%@", error);
        }];

Un altro caso d'uso più avanzato:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {

NSString *resultString = [NSString new];

NSException* customNilException = [NSException exceptionWithName:@"NilException"
                                                          reason:@"object is nil"
                                                        userInfo:nil];

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
                                                                reason:@"object is not a NSNumber"
                                                              userInfo:nil];

@try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    NSArray * array = dictFromData[@"someArrayKey"];

    for (NSInteger i=0; i < array.count; i++) {

        id resultString = array[i];

        if (![resultString isKindOfClass:NSNumber.class]) {

            [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;

            break;

        } else if (!resultString){

            @throw customNilException;        // <======

            break;
        }

    }

} @catch (SomeCustomException * sce) {
    // most specific type
    // handle exception ce
    //...
} @catch (CustomException * ce) {
    // most specific type
    // handle exception ce
    //...
} @catch (NSException *exception) {
    // less specific type

    // do whatever recovery is necessary at his level
    //...
    // rethrow the exception so it's handled at a higher level

    @throw (SomeCustomException * customException);

} @finally {
    // perform tasks necessary whether exception occurred or not

}

}


-7

Non vi è motivo di non utilizzare le eccezioni normalmente nell'obiettivo C anche per indicare le eccezioni alle regole di business. Apple può dire di usare NSError a chi importa. Obj C esiste da molto tempo e un tempo TUTTA la documentazione di C ++ diceva la stessa cosa. Il motivo per cui non importa quanto sia costoso lanciare e catturare un'eccezione, è che la durata di un'eccezione è estremamente breve e ... è un'ECCEZIONE al flusso normale. Non ho mai sentito nessuno dire mai in vita mia, quell'eccezione ha impiegato molto tempo a essere lanciata e catturata.

Inoltre, ci sono persone che pensano che l'obiettivo C stesso sia troppo costoso e invece codifichi in C o C ++. Quindi dire sempre usare NSError è mal informato e paranoico.

Ma alla domanda di questo thread non è ancora stata data risposta qual è il modo MIGLIORE per lanciare un'eccezione. I modi per restituire NSError sono ovvi.

Così è: [NSException raise: ... @throw [[NSException alloc] initWithName .... o @throw [[MyCustomException ...?

Uso la regola selezionata / deselezionata qui in modo leggermente diverso rispetto alla precedente.

La vera differenza tra (usando la metafora java qui) selezionata / deselezionata è importante -> se è possibile recuperare dall'eccezione. E per recupero intendo non solo NON crash.

Quindi uso le classi di eccezioni personalizzate con @throw per eccezioni recuperabili, perché è probabile che avrò un metodo app per cercare alcuni tipi di errori in più blocchi @catch. Ad esempio, se la mia app è un bancomat, avrei un blocco @catch per "WithdrawalRequestExceedsBalanceException".

Uso NSException: rilancio per le eccezioni di runtime poiché non ho modo di recuperare dall'eccezione, se non per prenderlo a un livello superiore e registrarlo. E non ha senso creare una classe personalizzata per questo.

Comunque è quello che faccio, ma se c'è un modo migliore, allo stesso modo espressivo, vorrei sapere anche io. Nel mio codice, da quando ho smesso di scrivere codice C molto tempo fa, non restituisco mai un errore NSE anche se ne ho passato uno da un'API.


4
Consiglierei di tentare di programmare un server con eccezioni come parte del normale flusso di casi di errore prima di fare dichiarazioni di generalizzazione come "non vi è alcun motivo per non utilizzare le eccezioni normalmente nell'obiettivo C". Che ci crediate o no, ci sono ragioni per scrivere applicazioni ad alte prestazioni (o almeno parti di applicazioni) in ObjC e generare eccezioni normalmente ostacola seriamente le prestazioni.
jbenet,

6
Ci sono davvero ottime ragioni per non usare le eccezioni nel cacao. Vedi la risposta di Bill Bumgarner qui per ulteriori informazioni: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Sa di cosa sta parlando (suggerimento: controlla il suo datore di lavoro). Le eccezioni in Cocoa sono trattate come errori irrecuperabili e possono lasciare il sistema in uno stato instabile. NSError è la strada da percorrere per superare errori generali.
Brad Larson

Le eccezioni sono eccezionali . I fallimenti delle regole aziendali sicuramente non si qualificano. "Trovare e progettare codice pesantemente eccezionale può portare a una vittoria decente." MSDN via codinghorror.com/blog/2004/10/…
Jonathan Watmough

3
Non è possibile generare eccezioni dai blocchi. Le eccezioni generate in un ambiente ARC potrebbero far perdere il programma. Quindi il downvote.
Moszi,

"Non importa quanto sia costoso lanciare e catturare un'eccezione" Sto scrivendo un emulatore in cui le prestazioni sono fondamentali. Non posso lanciare un sacco di costose eccezioni.
NobodyNada,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.