NSData dataWithBytesNoCopy из NSInputStream

Я работаю над перекодированием данных изображения из файла в строку с кодировкой base64, а затем обратно в байты, когда файл читается с использованием NSStream. Я думаю, что почти у цели, но я продолжаю сталкиваться с EXC_BAD_ACCESS в разные моменты во время преобразования.

Я новичок в мире NSStream и буферов, поэтому не стесняйтесь, дайте мне знать, если я использую здесь абсолютно неправильный подход.

Вот что у меня есть:

// Copy the bytes from our file input stream buffer
void *base64buffer = malloc(self.buffer[self.bufferOffset]);

// Convert the bytes to NSData for the base64 encode
NSData *dataToEncode = [NSData dataWithBytesNoCopy:base64buffer length:sizeof(base64buffer) freeWhenDone:YES];

// Convert our NSData into a base64 encoded string
NSString *base64EncodedData = [dataToEncode base64EncodedString];

// Convert our base64 encoded string back into NSData
NSData *encodedData = [base64EncodedData dataUsingEncoding:NSUTF8StringEncoding];

// Write the bytes to our output stream
bytesWritten = [self.producerStream write:[encodedData bytes] maxLength:[encodedData length]];

// Clean up
dataToEncode = nil;
base64EncodedData = nil;
encodedData = nil;
free(base64buffer);

person frsh    schedule 04.10.2010    source источник
comment
Во-первых, malloc не копирует байты, а выделяет новую память, так что это огромный провал; во-вторых, ваш плохой доступ происходит из-за двойного бесплатного доступа. Вы попросили создать NSData с помощью NoCopy, чтобы он использовал ваш буфер памяти в качестве резервного хранилища, и вы установили для параметра freeWhenDone значение YES, поэтому, когда этот объект NSData исчезает, он освобождает ваш буфер. Затем вы также явно освобождаете свой буфер; либо установите для этого параметра значение NO, либо не освобождайте base64buffer самостоятельно.   -  person Jason Coco    schedule 04.10.2010


Ответы (2)


Здесь несколько вопросов:

// Copy the bytes from our file input stream buffer
void *base64buffer = malloc(self.buffer[self.bufferOffset]);

Это не делает то, что говорит ваш комментарий. То, что вы только что сделали, это выделение буфера того же размера, что и size_t (4-байтовое или 8-байтовое целое число без знака) в self.buffer[self.bufferOffset]. По сути, вы пытаетесь выделить буфер практически случайного размера. Если размер слишком велик, вы вернете NULL, и это, вероятно, является причиной ваших сбоев.

// Convert the bytes to NSData for the base64 encode
NSData *dataToEncode = [NSData dataWithBytesNoCopy:base64buffer length:sizeof(base64buffer) freeWhenDone:YES];

Это не делает то, что вы думаете. sizeof(base64buffer) равно 4 или 8 в зависимости от того, компилируете ли вы в 32-битном или 64-битном режиме. Это размер указателя, а не размер буфера.

free(base64buffer);

Когда вы создали первоначальный NSData, вы сказали «да» «freeWhenDone». Это означает, что NSData стал владельцем base64buffer и попытается освободить его, когда он будет освобожден. Поскольку вы уже освободили его, эта операция потерпит неудачу непредсказуемым и неприятным образом.


Как мы можем это исправить? Попробуйте что-то вроде этого:

size_t bufferLengthInBytes = .... // you'll need to figure out how to get this

// Next, instead of mallocing my own buffer for the data, I'll let the runtime do it for me.
// I'm assuming that self.buffer is a pointer to the bytes you want to copy.
NSData* dataToEncode = [NSData dataWithBytes: self.buffer length:bufferLengthInBytes];

// I'm not sure where the method base64EncodedString comes from.  I assume you have a category to do this.
NSString *base64EncodedString = [dataToEncode base64EncodedString];

Остальная часть метода должна быть такой же, за исключением того, что у вас больше нет буфера для освобождения (на самом деле у вас его не было раньше).

person JeremyP    schedule 04.10.2010
comment
Спасибо. Это сработало прекрасно. Что касается bufferLengthInBytes, я использую: size_t bufferLengthInBytes = sizeof(self.buffer); Отдельное спасибо Торо, ваши советы мне тоже помогли. - person frsh; 04.10.2010
comment
@frsh: Хороший способ выразить благодарность Торо — проголосовать за его ответ. - person JeremyP; 05.10.2010

NSData *dataToEncode = [NSData dataWithBytesNoCopy:base64buffer length:sizeof(base64buffer) freeWhenDone:YES];

Эта строка будет освобождена (base64buffer), когда dataToEncode будет освобожден, потому что freeWhenDone:YES. Таким образом, вы освободите одно и то же пространство памяти дважды.

person AechoLiu    schedule 04.10.2010