ios - Perché è un errore "exc_bad_access" e non un errore "run-time" o "compile-time"?




objective-c xcode (3)

Perché è un exc_bad_access e non un errore in compile-time run-time o in compile-time ?

Per errore ho scritto "@age" invece di @"age" , e ha suscitato la mia curiosità.

Quello che capisco di exc_bad_access è che : Bad-Access è causato da un puntatore (okay reference) che è dereferenced a una locazione di memoria che non è ancora allocata o deallocata o non autorizzata ad accedere ( const o qualcosa).

Ma in questo caso sto scrivendo solo i dati sulla memoria e la sintassi non corrisponde al formato NS Objective-c . Quindi dovrebbe essere un errore di run-time invece di un Bad-Access .

Dove mi manca il concetto?


Come menzionato @"age" è una scorciatoia per creare un NSString * - che di nuovo è una sottoclasse di NSObject . Il @ davanti alla stringa indica al compilatore di creare e restituire un NSString * .

"@age" d'altra parte non ha il prefisso @ , e quindi il compilatore crea un const char * che è ciò che C usa per rappresentare le stringhe. Ricorda che Objective-C è un superset di C rigido, il che significa che qualsiasi codice C verrà compilato su un compilatore Objective-C. Ciò significa anche che il compilatore deve mantenere la retrocompatibilità per C, il che significa che "@age" è una stringa C mentre @"age" è un oggetto NSString di Objective-C Foundation Kit.

Per quanto riguarda la tua domanda: il motivo per cui questo non è un errore del compilatore, è dovuto al fatto che initWithObjects: initializer di NSArray non specifica il tipo richiesto. Dice semplicemente una lista di indicatori. E poiché entrambi @"age" ( NSString * ) è un puntatore e anche il "@age" ( const char * ) è un puntatore, il compilatore non si lamenterà. Entrambi sono tra l'altro initWithObjects: è definito dal compilatore.

Ora dici perché non si tratta di un errore in fase di esecuzione. EXC_BAD_ACCESS È un errore di runtime. Potresti voler dire perché non è stata lanciata un'eccezione, in quanto vi è una differenza nelle eccezioni e negli errori di runtime. Le eccezioni sono anche errori di run time, tuttavia, ma possono essere rilevate e gestite. I segnali (a cui EXC_BAD_ACCESS è) non possono in genere essere sottoposti e l'applicazione viene uccisa dal sistema operativo.

Il motivo per cui si tratta di un errore di run-time, è dovuto al fatto che initWithObject: si aspetta un elenco di NSObjects e la prima cosa che fa questa funzione è fare un controllo o lavorare sugli oggetti forniti. Ora internamente nel tempo di esecuzione di Objective-C è fondamentalmente una struct C con informazioni sui metodi e le variabili degli oggetti. Queste strutture in genere contengono come esempio un puntatore alla super classe dell'oggetto. Quello che stai dando a questo metodo è fondamentalmente spazzatura. Quando tenta di esaminare ciò che crede sia una struttura con puntatori e dati, tutto ciò che ottiene è un buffer di quattro byte ( "age\0" ). Quindi, quando tenta di dereferenziare di esempio un puntatore di super classe in quello che il metodo ritiene sia una struttura che leggerà al di fuori di quel buffer di quattro byte e quindi l'applicazione riceverà un EXC_BAD_ACCESS .

Così il gioco è fatto. Dal punto di vista del compilatore non stai facendo nulla di sbagliato. Dal punto di vista del runtime, stai alimentando la spazzatura, che non ha modo di rilevare. Funziona solo sui dati come se fosse ciò che si aspetta. E quando lo fa, va oltre i limiti del buffer che hai fornito e quindi ottiene un EXC_BAD_ACCESS .

Spero che questo abbia chiarito la tua curiosità.


Il motivo per cui ottieni EXC_BAD_ACCESS è che il metodo -initWithObjects: prevede che tutti i suoi argomenti siano oggetti Objective-C validi. Ogni oggetto Objective-C inizia con una piccola intestazione; questo era un puntatore semplice, chiamato isa , al suo oggetto di classe (non è necessariamente più così semplice, e in questi giorni non dovresti preoccuparti di te stesso: ci sono API di runtime Objective-C che puoi usare se necessario).

Il motivo per cui non si ottiene un errore del compilatore è che in C / C ++ / Objective-C non è possibile specificare i tipi corretti per un metodo o una funzione "varargs". Di conseguenza, il compilatore consente di passare argomenti di qualsiasi tipo, assumendo che tu sappia cosa stai facendo.

Ad ogni modo, all'interno dell'implementazione di -initWithObjects: tenterà di inviare un messaggio di -retain a ciascuno degli oggetti che si passano. Quando lo fa, tenterà di dereferenziare il puntatore isa . Nel caso della tua stringa C, significa che tratterà i primi quattro o otto byte della stringa come un puntatore. È improbabile che questo abbia un buon risultato e molto probabilmente otterrai EXC_BAD_ACCESS subito. Anche se sei fortunato e capita di puntare a una memoria valida, il runtime Objective-C si aspetta che punti a una struttura di Class valida, il che è tremendamente improbabile, e anche il risultato sarà molto probabilmente un EXC_BAD_ACCESS.


La sintassi @"name" è una stringa letterale ed [[NSString alloc] initWithUTF8String:"name\0"]; a dire [[NSString alloc] initWithUTF8String:"name\0"]; che lo rende un oggetto

"@age" d'altra parte, è un const char* . NSArray può gestire solo oggetti, quindi dargli un const char* causerà il crash della tua app. Non sono sicuro del perché l'analizzatore statico non rilevi questo problema in primo luogo, sono sorpreso che non ci sia almeno un avvertimento che ti dice di usare un letterale, come con NSLog();





exc-bad-access