I have a large malloc'd region that I want to wrap in an NSData object. Some time later, I make a copy of that NSData object. I want the two NSData objects to live independent lifetimes. ARC takes care of ref-counting the NSData objects themselves, but I'm trying to clarify the lifetime of the contained malloc'd region. Here's a code sketch:
float* cubeData = (float*)malloc(cubeDataSize);
printf("cubeData=%p\n", cubeData);
// cubeData=0x01beef00
for (...) { /* fill the cubeData array */ }
NSData* data = [NSData dataWithBytesNoCopy:cubeData length:cubeDataSize
freeWhenDone:YES];
NSData* data2 = [data copyWithZone:nil]
printf("data.bytes=%p data2.bytes=%p\n", data.bytes, data2.bytes);
// data.bytes=0x01beef00 data2.bytes=0x01beef00
It's OK with me that copyWithZone doesn't deep-copy the malloc'd region — I can use [NSData dataWithData:]
if I want a deep copy. What isn't clear to me (and I'm not sure how best to test) is which NSData object owns the underlying malloc'd buffer? If they both hold a reference to the malloc'd buffer (using some form of opaque reference counting) that's great! But if the malloc'd buffer gets freed when the data
object is released (as implied by freeWhenDone:YES
), data2
will have trouble on its hands.
Can someone explain what NSData does in this case? Alternatively, can someone suggest a definitive test to prove to myself what's going on?
To the underlying question:
Is the content of NSData separately reference-counted?
No. (But looking at your code, it shouldn't matter. See below after this diversion.)
--- Start Diversion ---
ARC manages retains and releases on Objective-C objects by sending the equivalent of retain
and release
messages at appropriate times. "Appropriate times" are determined at compile time by code inspection. That is exactly everything it does. When you start creating pointers to non-object pieces of those objects (i.e. bytes
), you're on your own to manage lifetime.
@CouchDeveloper provides good information about objc_precise_lifetime
. Placing this attribute on data objects can protect you from an ARC optimizations when dealing with internal pointers, but it isn't really relevant here. The point of objc_precise_lifetime
is to tell ARC it's not allowed to release an object before the referencing variable goes out of scope. The problem it solves looks like this:
NSData *data = ...;
void *stuff = data.bytes; // (1)
doSomething(stuff); // (2)
ARC has an optimization that says it's allowed to destroy data
between line (1) and line (2), since you never reference data
again, even though data
is in scope. Adding the objc_precise_lifetime
attribute forbids that optimization. When you start using NSData
a lot, this attribute can become important.
--- End Diversion ---
OK, but what about your situation?
float* cubeData = (float*)malloc(cubeDataSize);
NSData* data = [NSData dataWithBytesNoCopy:cubeData length:cubeDataSize freeWhenDone:YES];
NSData* data2 = [data copyWithZone:nil]
After this code has run, there are two possibilities (and you're not supposed to care most of the time which is true, since these are immutable objects):
data
and data2
are both strong pointers to the same NSData
object. That object owns the malloced memory and will free it when deallocated. (This is the one that's almost certain to happen in this particular case, but that's an implementation detail.)data
points to an NSData
object that owns the malloced memory and will free it when deallocated. data2
points to a different NSData
object with its own memory (which it will free when it is deallocated.)(There are other options; perhaps NSData
uses an underlying dispatch_data
or a copy-on-write scheme. But all the options should effectively look like the above from the outside.)
In the first case, if data
goes out of scope, but data2
is still around, then the owning NSData
is preserved. No problem. In the second case, when data
goes out of scope, it destroys its memory, but data2
has an independent copy of it, so again no problem.
I think your confusion is coming from thinking that data
owns the memory. It doesn't. The NSData
object that data
points to owns the memory. data
and data2
are just pointers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With