Re: Accessing buffers in NSData/NSMutableData under garbage collection
Re: Accessing buffers in NSData/NSMutableData under garbage collection
- Subject: Re: Accessing buffers in NSData/NSMutableData under garbage collection
- From: Rick Hoge <email@hidden>
- Date: Tue, 19 Feb 2008 09:37:08 -0500
Thanks for the helpful replies -
From the replies and further experimentation I think I now
understand that:
1) The code fragment in my original post *will* break if
optimization is turned on, but not if it is off:
-(IBAction)danglePointer:(id)sender {
int size = 1e6;
NSMutableData *testData = [NSMutableData
dataWithLength:sizeof(float)*size];
float *testPointer = (float*)[testData mutableBytes]; // Note that
__strong makes no difference here
[[NSGarbageCollector defaultCollector] collectExhaustively];
sleep(1); // If there is no delay, I was not able to get a crash
(in 20-30 trials)
BOOL flag = YES;
if (flag) {
testPointer[0] = 3; // If optimization is turned on, the code
crashes at this line every time
NSLog(@"testPointer = %f",testPointer[0]);
}
}
Also, with optimization turned on, the above code only crashes if I
insert the delay between the -collectExhaustively call. Without the
delay, I can happily call this method over and over again (with
optimization on) without getting a crash. Maybe this is a reflection
of the fact that the collector runs in another thread, and needs time
to complete the collection. Note also that it's clearly not just
scope rules that determine when collection can take place, since
testData is still in scope at the crashing line of code. Note also
that using __strong in front of the pointer declaration makes no
difference, since the collector cares about references to the object
and not to its internal structures. The above snippet (with delay)
better represents my real apps, in which there may be a lot of
processing work done between instantiation of the NSMutableData and
later use of the pointer. In my haste to put together a tiny test
case, I built and ran it in "Debug" mode the first time (optimizations
off), which appears to be why I couldn't get the crash. Of course
replacing the two lines including testPointer[0] with
*((float*)[testData mutableBytes] + 0) = 3.0;
NSLog(@"testPointer = %f",*((float*)[testData mutableBytes] + 0));
runs fine under optimization.
2) NSAllocateCollectible() is probably a more appropriate choice in
the above example. I have tested a number of scenarios running the
Object Allocation template in Instruments to test apps with large
blocks allocated in this way, and the memory footprint of the app
appears to be controlled quite effectively under GC.
3) You can declare pointer instance variables using the __strong
qualifier and these can point to blocks allocated with
NSAllocateCollectible(). The memory will live as long as the object,
and is freed when the object is reclaimed (sorry if this is obvious,
but I had to test it to be sure myself). I was able to create a GC
doc-based apps in which the docs 'own' 100MB memory buffers allocated
in the above way - these have the appropriate lifetime and are
reclaimed at the appropriate time.
Having realized the above points, this gets back to part of my
original question regarding programming style (rather than
correctness) under GC. On this note I'm still trying to figure out:
1) What potential pitfalls might arise if I start using
NSAllocateCollectible to create buffers where I used to use NSData
objects? The obvious differences, which may not matter depending on
the app, seem to be:
- can't put the buffer into a collection (NSArray, NSDictionary,
etc.) as easily as an NSData/NSMutableData
- less obvious how KVC should work
(before, if I updated an NSData or NSMutableData object, I'd use
a setXXX method and KVC would just work.
with a buffer, I could probably use -willChangeValueForKey:/-
didChangeValueForKey: so this is probably not a huge deal)
- lose the ease of doing a -copy and -mutableCopy to replicate the
memory chunk (not much more work with a buffer, though)
- lose the serialization and archiving methods of NSData/
NSMutableData
2) As mentioned above, there may be situations where objects want to
be notified when the data contained in a buffer allocated with
NSAllocateCollectible() (and referenced by a pointer instance variable
in another object) changes. One simple approach would be to make sure
code that modifies the memory is wrapped with -willChangeValueForKey:/-
didChangeValueForKey: calls. This will almost surely work, but I
wonder if this is considered good style (since I may not really be
changing the pointer value, just values in the memory that it points
to). Any comments on this or suggestions for better ways would be
welcome.
Thanks again,
Rick
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden