Я получал ошибку EXC_BAD_ACCESS, казалось бы, без причины. После долгой отладки я нашел проблему, но понятия не имею, почему она возникает. Вот несколько примеров, когда это происходит:
@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);
? Я думал, что это то же самое.
@Willeke Спасибо, что сообщили мне, я действительно перепутал это, упрощая код. Я обновил предоставленный код, теперь он больше похож на исходный код, но я все равно удалил части, поскольку многие из них не связаны с проблемой. Если вы не можете воспроизвести проблему и хотите получить полный исходный код, я могу предоставить вам все это.
Именно сбой char buffer[10]
в исходных примерах нас всех сбил с толку. Помещение 10 байт в стек всегда не будет проблемой. Но 200 МБ легко могут вызвать проблемы. А malloc
, обращаясь к памяти из кучи, позволяет избежать ограничений памяти стека. Спасибо за разъяснение вопроса. Итак, Крис ответил на ваш вопрос?
Несколько порядков, чтобы рассмотреть, что вам следует выделить в стеке, а не в куче: IIRC, размер стека составляет 1 МБ в iOS и по умолчанию равен 8 МБ в macOS (хотя его можно переопределить с помощью параметров компоновщика). Обычно я бы воздерживался от выделения стека размером более одного-двух КБ, за исключением самых исключительных случаев.
Почему один из них не работает в вашей ситуации, требует более подробной информации, чтобы ответить окончательно.
В чем разница между
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?
Вы можете free
только то, что было выделено с помощью malloc
, realloc
или calloc
.
В C вы можете иметь в стеке массивы переменной длины, где длина определяется переменной. Однако имейте в виду, что пространство стека ограничено, поэтому убедитесь, что переменная относительно мала.
спасибо за информацию, вероятно, именно этот размер вызывает проблемы в стеке. Я не думаю, что в Objective-C существует бесплатная версия SDK для Mac OS старше 10.13 High Sierra. Я не могу найти никакой информации о том, как использовать бесплатно в Objective-C для более раннего SDK. Должен быть другой способ освободить память: если я запускаю свое приложение в инструментах, я вижу много памяти, хранимой malloc, которая, похоже, не освобождается после завершения функции и исчезает только при перезапуске приложения. ARC не видит возможности malloc, и я не могу использовать стандартные функции C.
Я перекомпилировал код и теперь free внезапно работает как надо, я посмотрел в Инструментах и есть явная разница, память сразу падает почти до 0, когда я все освобождаю, как только в ней больше нет необходимости. Я не знаю, почему Xcode описывает это как нечто подходящее для IOAudioPort, поскольку оно работает для всего. В документации Apple указана версия 10.13 и выше, но у меня она работает нормально, и я не знаю, почему некоторые функции C недоступны в Objective-C. Я наконец-то начинаю понимать стек и накапливаю еще немного. Большое спасибо за помощь!
Ваш код не компилируется, выложите реальный код, пожалуйста (возможно
[data getBytes:buffer length:10]
).NSFileHandle *handle = […]
дает сбой? Я не вижу свойства в вашем коде.