Поведение NSData initWithBytesNoCopy: length: freeWhenDone: - PullRequest
6 голосов
/ 01 января 2012

Я хочу иметь общий буфер данных с изменяемой длиной и вот как я могу его создать:

void *buffer = malloc(length);
// initialize buffer content
NSData *sharedData = [[NSData alloc] initWithBytesNoCopy:buffer length:length freeWhenDone:YES]

Что произойдет, если я изменю buffer после того, как создаю из него NSData? Отражает ли NSData изменение, которое я внес в buffer?

Я могу гарантировать, что sharedData не получит dealloc, когда я хочу изменить buffer.

Вот как я на самом деле хочу это использовать:


void *my_alloc(CFIndex allocSize, CFOptionFlags hint, void *info) {return NULL;}
void my_dealloc(void *ptr, void *info) {
    mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)ptr, (size_t)info);
}

size_t length = //some number
mach_vm_address_t buffer;
mach_vm_allocate(mach_task_self(), &buffer, length, VM_FLAGS_ANYWHERE);
// do something to buffer, for example pass to other process using mach RPC and expect other process will modify the content
CFAllocatorContext context = {0, (void *)length, NULL, NULL, NULL, my_alloc, NULL, my_dealloc, NULL};
CFAllocatorRef allocator = CFAllocatorCreate(NULL, &context);
CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)buffer, length, allocator);

1 Ответ

8 голосов
/ 01 января 2012

initWithBytesNoCopy: эффективно создаст оболочку NSData вокруг существующего буфера;так что да, вещи, получающие доступ через [sharedData bytes], будут видеть любые сделанные вами обновления.

Конечно, он ничего не делает для связывания других объектов, созданных из экземпляра NSData, поэтому, например, [NSImage initWithData:sharedData] можетсделайте копию для экземпляра NSImage, которая не будет отражать никаких изменений.

Кроме того, с freeWhenDone:YES NSData уничтожит буфер при удалении последней ссылки, так что следите за этим=)


Итак, учитывая, что NSData фактически является тонкой оболочкой для выделения malloc(), да, он будет отражать изменения, внесенные в эту память (любым процессом);но так как он будет вызывать free() для него, будет плохой идеей использовать его для переноса буфера, созданного другим способом (mach_vm_allocate) с помощью freeWhenDone:YES.

Если вам это действительно не нужноиспользовать пользовательский распределитель (почему?), я думаю, вам лучше использовать:

NSMutableData* sharedData = [NSMutableData dataWithCapacity:length];
// `dataWithLength:` will fully allocate and zero the buffer first, if you prefer
void* buffer = [sharedData mutableBytes];
// do something to `buffer`, mach RPC, etc.
// 3: profit.
...