Подтвердить что ты не робот

Почему это "exc_bad_access", а не ошибка "время выполнения" или "время компиляции"?

ScreenShot : running mode of xCode 5.1

Почему это ошибка exc_bad_access, а не run-time или compile-time?

По ошибке я написал "@age" вместо @"age", и это вызвало мое любопытство.

То, что я понимаю в exc_bad_access, состоит в том, что: Bad-Access вызвано указателем (okay reference), который dereferenced, в ячейку памяти, которая либо еще не выделена, либо освобождена или несанкционированный доступ (const или что-то еще).

Но в этом случае я только записываю данные в память, а синтаксис не соответствует формату NS Objective-c. Следовательно, это должна быть ошибка времени выполнения вместо Bad-Access.

Где мне не хватает понятия?

4b9b3361

Ответ 1

Причина, по которой вы получаете EXC_BAD_ACCESS, заключается в том, что метод -initWithObjects: ожидает, что все его аргументы будут действительными Objective-C объектами. Каждый объект Objective-C начинается с небольшого заголовка; это был простой указатель, называемый isa, его объекту класса (это уже не так уж просто, и в наши дни вы не должны думать о себе, есть Objective-C API-интерфейсы времени выполнения, которые вы можете использовать вместо этого, если необходимо).

Причина, по которой вы не получаете ошибку компилятора, заключается в том, что в C/С++/ Objective-C нет способа указать правильные типы для метода или функции "varargs". В результате компилятор позволяет передавать аргументы любого типа, предполагая, что вы знаете, что вы делаете.

В любом случае, в рамках реализации -initWithObjects:, он попытается отправить сообщение -retain каждому из объектов, которые вы передаете. Когда он это сделает, он попытается разыменовать указатель isa. В случае вашей строки C это означает, что он будет обрабатывать первые четыре или восемь байтов строки в качестве указателя. Это очень маловероятно, чтобы иметь хороший результат, и, скорее всего, вы сразу же получите EXC_BAD_ACCESS. Даже если вам повезло, и они действительно указывают на действительную память, среда выполнения Objective-C ожидает, что они укажут на действительную структуру Class, что крайне маловероятно, и результат этого тоже очень вероятно для EXC_BAD_ACCESS.

Ответ 2

Фактически, "@age" является const char*, поэтому он, похоже, соответствует вашему описанию того, что является exc_bad_access.

Ответ 3

Как упоминалось, @"age" является ярлыком для создания NSString *, который снова является подклассом NSObject. @ перед строкой сообщает компилятору создать и вернуть NSString *.

"@age", с другой стороны, не имеет префикса @, поэтому компилятор создает const char *, который используется C для представления строк. Помните, что Objective-C является строгим надмножеством C, что означает, что любой код C будет компилироваться в компиляторе Objective-C. Это также означает, что компилятор должен поддерживать обратную совместимость для C, что опять означает, что "@age" является строкой C, тогда как @"age" является объектом NSString Foundation Objective-C.

В отношении вашего вопроса: причина, по которой это не ошибка компилятора, заключается в том, что инициализатор initWithObjects: NSArray не задает требуемый тип. Он просто говорит список указателей. И поскольку оба @"age" (NSString *) являются указателями, а "@age" (const char *) также является указателем, компилятор не будет жаловаться. Оба они, кстати, определены initWithObjects:, разрешенными компилятором.

Теперь вы говорите, почему это не ошибка времени выполнения. EXC_BAD_ACCESS Является ошибкой времени выполнения. Вы могли бы означать, почему не возникает исключение, так как существует разница в исключениях и ошибках времени выполнения. Исключения также являются ошибками времени выполнения, но они могут быть обнаружены и приняты. Сигналы (которые a EXC_BAD_ACCESS), как правило, не могут быть приняты, и приложение уничтожается операционной системой.

Причина, по которой это ошибка во время выполнения, заключается в том, что initWithObject: ожидает список NSObjects, и первое, что делает эта функция, - это провести некоторую проверку или работу над предоставленными объектами. Теперь внутренне в Objective-C время выполнения это в основном C struct с информацией об объектах методов и переменных. Эти структуры обычно содержат в качестве примера указатель на суперкласс объекта. То, что вы даете этому методу, в основном - мусор. Когда он пытается исследовать то, что, по его мнению, является структурой с указателями и данными - все, что он получает, это четырехбайтовый буфер ("age\0"). Поэтому, когда он пытается в качестве примера разыменовать указатель суперкласса в том, что, по мнению метода, является структурой, он будет читать за пределами этого четырехбайтового буфера, и поэтому ваше приложение получит EXC_BAD_ACCESS.

Итак, у вас это есть. С точки зрения компилятора вы ничего не делаете неправильно. С точки зрения времени вы кормите его мусором, который у него нет средств для обнаружения. Он просто работает над данными, как будто это то, чего он ожидает. И когда он это делает, он выходит за границы выделенного вами буфера и поэтому получает EXC_BAD_ACCESS.

Надеюсь, это прояснило ваше любопытство.

Ответ 4

Синтаксис @"name" является строковым литералом и совпадает с выражением [[NSString alloc] initWithUTF8String:"name\0"];, которое делает его объектом

"@age", с другой стороны, является const char*. NSArray может обрабатывать объекты, поэтому предоставление const char* приведет к сбою приложения. Я не уверен, почему статический анализатор не поймает эту проблему в первую очередь, я удивлен, что нет хотя бы предупреждения, говорящего вам использовать литерал, например, с NSLog();