Инициализация массива символов Objective-C вызывает EXC_BAD_ACCESS при доступе к свойству

Я получал ошибку EXC_BAD_ACCESS, казалось бы, без причины. После долгой отладки я нашел проблему, но понятия не имею, почему она возникает. Вот несколько примеров, когда это происходит:

  • МойОбъект.h
@interface MyFile : NSObject

@property (strong) NSFileHandle *handle;

@end

@interface MyClass : NSObject
{
    int bufferSize;
    NSMutableArray *files;
}

- (void)readFile1;
- (void)readFile2;
- (void)readFile3;
- (void)readFile4;

@end
  • МойОбъект.м
@implementation MyObject

- (id)init
{
    bufferSize = 200000000;
    // Initialize files array, add a file object and open the file handle.
}

- (void)readFile1
{
    UInt64 bytesToProcess;
    file = [files objectAtIndex:0];
    char *buffer = malloc(bufferSize);
    
    do @autoreleasepool {
        NSFileHandle *handle = [file handle];
        bytesToProcess = [data = [handle readDataOfLength:bufferSize] length];
        [data getBytes:buffer length:bytesToProcess]; // This works fine.
        // Modify the buffer.
    } while (bytesToProcess);
}

- (void)readFile2
{
    UInt64 bytesToProcess;
    file = [files objectAtIndex:0];
    char buffer[bufferSize];
    
    do @autoreleasepool {
        NSFileHandle *handle = [file handle]; // This throws EXC_BAD_ACCESS.
        bytesToProcess = [data = [handle readDataOfLength:bufferSize] length];
        [data getBytes:buffer length:bytesToProcess];
        // Modify the buffer.
    } while (bytesToProcess);
}

- (void)readFile3
{
    UInt64 bytesToProcess;
    file = [files objectAtIndex:0];
    char buffer[10];
    
    do @autoreleasepool {
        NSFileHandle *handle = [file handle];
        bytesToProcess = [data = [handle readDataOfLength:10] length];
        [data getBytes:buffer length:bytesToProcess]; // This works fine.
        // Modify the buffer.
    } while (bytesToProcess);
}

- (void)readFile4
{
    UInt64 bytesToProcess;
    file = [files objectAtIndex:0];
    
    do @autoreleasepool {
        NSFileHandle *handle = [file handle];
        bytesToProcess = [data = [handle readDataOfLength:bufferSize] length];
        char buffer[bufferSize];
        [data getBytes:buffer length:bytesToProcess]; // This throws EXC_BAD_ACCESS.
        // Modify the buffer.
    } while (bytesToProcess);
}

@end

Я не понимаю, почему это стало проблемой из ниоткуда, код работал долгое время и внезапно перестал работать. Я предполагаю, что malloc распределяет память немного по-другому. Я не понимаю, как инициализация массива делает мой объект недоступным, я не ожидаю, что он перезапишет выделенную память. В чем разница между char buffer[10]; и char *buffer = malloc(10);? Я думал, что это то же самое.

Ваш код не компилируется, выложите реальный код, пожалуйста (возможно [data getBytes:buffer length:10]). NSFileHandle *handle = […] дает сбой? Я не вижу свойства в вашем коде.

Willeke 20.04.2024 09:57

@Willeke Спасибо, что сообщили мне, я действительно перепутал это, упрощая код. Я обновил предоставленный код, теперь он больше похож на исходный код, но я все равно удалил части, поскольку многие из них не связаны с проблемой. Если вы не можете воспроизвести проблему и хотите получить полный исходный код, я могу предоставить вам все это.

Foxyz 20.04.2024 14:32

Именно сбой char buffer[10] в исходных примерах нас всех сбил с толку. Помещение 10 байт в стек всегда не будет проблемой. Но 200 МБ легко могут вызвать проблемы. А malloc, обращаясь к памяти из кучи, позволяет избежать ограничений памяти стека. Спасибо за разъяснение вопроса. Итак, Крис ответил на ваш вопрос?

Rob 20.04.2024 18:32

Несколько порядков, чтобы рассмотреть, что вам следует выделить в стеке, а не в куче: IIRC, размер стека составляет 1 МБ в iOS и по умолчанию равен 8 МБ в macOS (хотя его можно переопределить с помощью параметров компоновщика). Обычно я бы воздерживался от выделения стека размером более одного-двух КБ, за исключением самых исключительных случаев.

Rob 20.04.2024 19:13
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему один из них не работает в вашей ситуации, требует более подробной информации, чтобы ответить окончательно.

В чем разница между char buffer[10]; и char *buffer = malloc(10);? Я думал, что это то же самое.

На этот вопрос больше ответа.

В первом случае десять байт автоматически выделяются в стеке, если они расположены внутри функции. Попытка вернуть указатель на эту память outside функции приведет к неопределенному поведению, поскольку эта память может быть повторно использована другим вызовом функции в стеке и, следовательно, больше не действительна вне вызова функции.

В последнем случае с помощью malloc память выделяется динамически. Указатель на него можно вернуть из функции и использовать в другой функции. Однако память не освобождается автоматически, и ее необходимо освободить намеренно с помощью free.

В зависимости от того, как вы используете эту память, это вполне может объяснить, почему использование malloc работает, а char buffer[10]; нет. Я настоятельно рекомендую компилировать с включенными предупреждениями.

Пространство стека

С помощью вашего отредактированного кода, где bufferSize находится 200000000, мы теперь видим, что в readFile2 и readFile4 вы пытаетесь поместить в стек двести миллионов байт. Это ситуация, когда вам действительно понадобится динамическое распределение.

Даже при динамическом распределении памяти вы пытаетесь разместить двести миллионов байт в одном блоке. Это много памяти. Возможно, вы захотите переосмыслить свой подход и посмотреть, сможете ли вы работать меньшими порциями.

Должны ли данные в стеке иметь постоянный размер или размер можно определить с помощью переменной, которая может меняться в зависимости от предыдущего кода? Спасибо за объяснение разницы, теперь это имеет больше смысла. У меня есть опыт работы с управляемыми языками, поэтому я не привык к ручному управлению памятью. Если я попытаюсь использовать free(buffer), я получаю сообщение об ошибке «Ожидаемое выражение», а описание бесплатной функции — «free(void *) Освобождает все ресурсы, выделенные IOAudioPort». Может быть, мне придется использовать альтернативу бесплатному в Objective-C?

Foxyz 20.04.2024 14:39

Вы можете free только то, что было выделено с помощью malloc, realloc или calloc.

Chris 20.04.2024 15:29

В C вы можете иметь в стеке массивы переменной длины, где длина определяется переменной. Однако имейте в виду, что пространство стека ограничено, поэтому убедитесь, что переменная относительно мала.

Chris 20.04.2024 15:30

спасибо за информацию, вероятно, именно этот размер вызывает проблемы в стеке. Я не думаю, что в Objective-C существует бесплатная версия SDK для Mac OS старше 10.13 High Sierra. Я не могу найти никакой информации о том, как использовать бесплатно в Objective-C для более раннего SDK. Должен быть другой способ освободить память: если я запускаю свое приложение в инструментах, я вижу много памяти, хранимой malloc, которая, похоже, не освобождается после завершения функции и исчезает только при перезапуске приложения. ARC не видит возможности malloc, и я не могу использовать стандартные функции C.

Foxyz 20.04.2024 17:19

Я перекомпилировал код и теперь free внезапно работает как надо, я посмотрел в Инструментах и ​​есть явная разница, память сразу падает почти до 0, когда я все освобождаю, как только в ней больше нет необходимости. Я не знаю, почему Xcode описывает это как нечто подходящее для IOAudioPort, поскольку оно работает для всего. В документации Apple указана версия 10.13 и выше, но у меня она работает нормально, и я не знаю, почему некоторые функции C недоступны в Objective-C. Я наконец-то начинаю понимать стек и накапливаю еще немного. Большое спасибо за помощь!

Foxyz 20.04.2024 17:37

Другие вопросы по теме