• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Accessing buffers in NSData/NSMutableData under garbage collection
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Accessing buffers in NSData/NSMutableData under garbage collection


  • Subject: Re: Accessing buffers in NSData/NSMutableData under garbage collection
  • From: Alastair Houghton <email@hidden>
  • Date: Tue, 19 Feb 2008 15:03:35 +0000

On 19 Feb 2008, at 14:37, Rick Hoge wrote:

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]);
}
}



[snip]

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.

Indeed. The problem is that the compiler has elided the testData variable. If you add another use of the testData variable after the "if (flag)" statement, you'll find that it will stop crashing.


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.

Using __strong would make no difference in *any* case because it does nothing for local variables. The collector *always* scans them and they can't contain intergenerational pointers (by definition), so there would be no point in a write barrier.


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.

Indeed. All you have to do is make sure that the compiler hasn't removed all references to the object. Sticking a "volatile" on the NSMutableData pointer, i.e.


NSMutableData * volatile testData = [NSMutableData dataWithLength:sizeof(float)*size];

(N.B. *after* the '*', not before) will force the matter.

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.

It has a similar problem to NSMutableData if you're using it this way. That is, you need to keep hold of a pointer to the start of the buffer, and you mustn't allow the compiler to optimise it away.


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.

Yes.

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

You still have the problem that the compiler might elide the pointer under optimization. You need either to use "volatile" or to store the pointer into a global or instance variable that has been marked __strong.


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.

I think what you propose here is pragmatic; it also matches the kind of behaviour you'd expect to see if you had e.g. an NSMutableString instance variable, surely?


But I'd stick with NSData, I think. It seems to have advantages and no significant disadvantages.

I would write more, but my hand is hurting again :-(

Kind regards,

Alastair.

--
http://alastairs-place.net


_______________________________________________

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


References: 
 >Re: Accessing buffers in NSData/NSMutableData under garbage collection (From: Gordon Apple <email@hidden>)
 >Re: Accessing buffers in NSData/NSMutableData under garbage collection (From: Rick Hoge <email@hidden>)
 >Re: Accessing buffers in NSData/NSMutableData under garbage collection (From: Mika Ryynänen <email@hidden>)
 >Re: Accessing buffers in NSData/NSMutableData under garbage collection (From: Rick Hoge <email@hidden>)

  • Prev by Date: Re: Accessing buffers in NSData/NSMutableData under garbage collection
  • Next by Date: Re: Low latency video mapping on 3D objects
  • Previous by thread: Re: Accessing buffers in NSData/NSMutableData under garbage collection
  • Next by thread: Binding an NSTableView's checked rows to user's preferences.
  • Index(es):
    • Date
    • Thread