ios - ¿Por qué es un error "exc_bad_access" y no un error "en tiempo de ejecución" o "tiempo de compilación"?
objective-c xcode (4)
Como se mencionó @"age"
es un atajo para crear un NSString *
, que nuevamente es una subclase de NSObject
. El @
delante de la cadena le dice al compilador que cree y devuelva un NSString *
.
"@age"
por otro lado no tiene el prefijo @
, y por lo tanto el compilador crea un const char *
que es lo que C usa para representar las cadenas. Recuerde que Objective-C es un superconjunto estricto de C, lo que significa que cualquier código C se compilará en un compilador de Objective-C. Esto también significa que el compilador necesita mantener la compatibilidad hacia atrás para C, lo que significa que "@age"
es una cadena C mientras que @"age"
es un objeto Objective-C Foundation Kit NSString.
En lo que respecta a su pregunta: La razón por la cual este no es un error del compilador, se debe a que initWithObjects:
initializer de NSArray no especifica el tipo requerido. Simplemente dice una lista de punteros. Y como @"age"
( NSString *
) es un puntero y "@age"
( const char *
) también es un puntero, el compilador no se quejará. Ambos son, por cierto, los initWithObjects:
definidos por el compilador.
Ahora dices por qué no es un error en tiempo de ejecución. EXC_BAD_ACCESS
ES un error de tiempo de ejecución. Puede querer decir por qué no se lanza una excepción, ya que existe una diferencia en las excepciones y los errores de tiempo de ejecución. Las excepciones también son errores de tiempo de ejecución, pero se pueden detectar y actuar en consecuencia. Las señales (que es EXC_BAD_ACCESS
) normalmente no se pueden aplicar y el sistema operativo EXC_BAD_ACCESS
la aplicación.
La razón por la cual es un error en tiempo de ejecución, es porque initWithObject:
están esperando una lista de NSObjects
y lo primero que hace esta función es hacer alguna inspección o trabajar en los objetos proporcionados. Ahora internamente en el tiempo de ejecución de Objective-C es básicamente una struct
C con información sobre los métodos y las variables de los objetos. Estas estructuras típicamente contienen como un ejemplo un puntero a la súper clase del objeto. Lo que le estás dando a este método es básicamente basura. Cuando intenta examinar lo que cree que es una estructura con punteros y datos, todo lo que obtiene es un búfer de cuatro bytes ( "age/0"
). Entonces, cuando intente, como ejemplo, desreferenciar un puntero de superclase en lo que el método cree que es una estructura, leerá fuera de ese búfer de cuatro bytes y, por lo tanto, su aplicación recibirá un EXC_BAD_ACCESS
.
Entonces ahí lo tienes. Desde el punto de vista del compilador no está haciendo nada mal. Desde el punto de vista del tiempo de ejecución, lo está alimentando basura, que no tiene forma de detectar. Simplemente funciona trabajando en los datos como si fuera lo que espera. Y cuando lo hace, sale de los límites del búfer que ha proporcionado y, por lo tanto, obtiene un EXC_BAD_ACCESS
.
Espero que esto aclare tu curiosidad.
¿Por qué es un exc_bad_access
y no un error en compile-time
run-time
o en run-time
compile-time
?
Por error, escribí "@age"
lugar de @"age"
, y despertó mi curiosidad.
Lo que entiendo de exc_bad_access
es que : Bad-Access
es causado por un puntero (referencia correcta) que se dereferenced
a una ubicación de memoria que aún no está asignada o desasignada o no autorizada para acceder ( const
o algo así).
Pero en este caso solo estoy escribiendo datos en la memoria y la sintaxis no coincide con el formato NS Objective-c
. Por lo tanto, debería ser un error en tiempo de ejecución en lugar de un mal acceso .
¿Dónde me estoy perdiendo el concepto?
De hecho, "@age"
es un const char*
por lo que parece coincidir con tu descripción de lo que es un exc_bad_access
.
La razón por la que obtiene EXC_BAD_ACCESS es que el método -initWithObjects:
espera que todos sus argumentos sean objetos Objective-C válidos. Cada objeto Objective-C comienza con un pequeño encabezado; esto solía ser un puntero directo, llamado isa
, para su objeto de clase (ya no es tan simple, y en estos días no debes preocuparte por ti mismo, hay API de tiempo de ejecución de Objective-C que puedes usar si necesario).
La razón por la que no se obtiene un error de compilación aquí es que no hay forma de que en C / C ++ / Objective-C se especifiquen los tipos correctos para un método o función "varargs". Como resultado, el compilador le permite pasar argumentos de cualquier tipo, suponiendo que sepa lo que está haciendo.
De todos modos, dentro de la implementación de -initWithObjects:
intentará enviar un mensaje de " -retain
" a cada uno de los objetos que pasa. Cuando lo haga, intentará desreferenciar el puntero isa
. En el caso de su cadena C, eso significa que tratará los primeros cuatro u ocho bytes de la cadena como un puntero. Es muy poco probable que tenga un buen resultado, y es muy probable que obtenga EXC_BAD_ACCESS de inmediato. Incluso si tuvo suerte y apuntan a una memoria válida, el tiempo de ejecución de Objective-C esperará que apunten a una estructura de Class
válida, lo cual es tremendamente improbable, y el resultado de eso también será muy probable. un EXC_BAD_ACCESS.
La sintaxis @"name"
es una cadena literal y es lo mismo que decir [[NSString alloc] initWithUTF8String:"name/0"];
lo que lo convierte en un objeto
"@age"
por otro lado, es un const char*
. NSArray
solo puede manejar objetos, por lo que const char*
hará que la aplicación se bloquee. No estoy seguro de por qué el analizador estático no capta este problema en primer lugar, me sorprende que no haya al menos una advertencia que le NSLog();
que use un literal, como con NSLog();