views:

49

answers:

2

Hi I need to initialize an NSObject at a particular location that I specify(through a void* pointer, for example). For a little bit of context, I am writing a sqlite3 aggregate function. To keep temporary results from this function, I have to call a sqlite3_aggregate_context function, which allocates a block of memory for me. I want to store an NSDecimalNumber at this location.

So far I have tried two approaches:

1)allocWithZone, by doing:

void *location = sqlite3_aggregate_context(...); //returns a block of allocated memory of a certain size

NSDecimalNumber *num = [[NSDecimalNumber allocWithZone:NSZoneFromPointer(location)] initWithInt:0];

This does not work because NSZoneFromPointer returns nil. Docs say that the arguments to this function must be a previously allocated pointer, which it is. I dont know if this means allocated using NSZoneMalloc/Calloc.

2)

id location = sqlite3_aggregate_function(...);

location = [[NSDecimalNumber alloc] init];

but this causes some kind of infinite recursion when freeing the memory...not sure what the deal is. A screenshot here: http://dl.dropbox.com/u/3002073/Public%20Sync/sqlitefunctionissue.png

Any suggestions will be greatly appreciated!

+1  A: 

Your first version is simply not going to work. NSZoneFromPointer only works when passed a pointer allocated from a zone. It's used so you can allocate an object from the same zone as some other object.

The second version ought to work, though it's difficult to tell without more context. What are you passing to sqlite3_aggregate_context as the size of the memory to allocate? And how are you freeing that memory when you're done?

The second version doesn't work because the "id" type is actually a pointer, so you're pointing it at the memory returned by sqlite3_aggregate_context(), then pointing it at the memory returned by alloc/init. You really need to store a pointer-to-pointer to get that to work the way you want.

NSDecimalNumber is an immutable class, so calling -init on it (as opposed to -initWithDecimal:) is just going to get you some default value. What sort of code are you using to replace the NSNumber with new values as the function progresses?

More to the point, why use NSDecimalNumber at all, as opposed to a C integer, or double, or whatever?

Mark Bessey
mark, the arguments to sqlite3_aggregate_context is basically the size of memory you would like to allocate. There is a callback for cleanup, so I can reliably clean up the nsdecimalnumber. I a using NSDecimalNumber because I am doing calculations on fractions of pennies and need to preserve precision.
Ying
+4  A: 

You can't really determine reliably where an object is going to be created in memory. The NSZoneFromPointer fails for you because the sqlite3 API is not using zones to allocate its resources.

If you want to be able to pass a specific location, you should do so using a pointer to the object (so you are storing a pointer to a pointer basically). You can then read this information from your aggregate function and update it accordingly. Just make sure that you don't simply let your object be freed at the end of the call without taking care to release it (or you'll have a leak).

So, for example, you could do something like:

NSDecimalNumber** numberLocation = sqlite3_aggregate_context(...);
*numberLocation = [[NSDecimalNumber alloc] initWithDouble:25.0];

You now have a reference to your object stored in your special memory area and can access it any time:

NSDecimalNumber* storedNumber = *numberLocation;
NSDecimalNumber* computedNumber = [[NSDecimalNumber alloc] initWithDouble:[storedNumber doubleValue] * someComputation];
[storedNumber autorelease];
*numberLocation = computedNumber;

On the other hand, I agree with Mark; maybe this immutable class isn't the best solution to your problem?

Jason Coco
I actually posted this question and didnt get back much information: http://stackoverflow.com/questions/3919127/how-should-nsdecimalnumbers-be-stored-in-the-database. I need to store nsdecimalnumber because I am working with currencies and cannot afford to lose precision with double types when writing to the database. I am storing them as strings and then reading them out as nsdecimal number to do summation in an aggregate function. Hence the need to store a temporary sum of variables so far. I guess with this approach i would have to release and create a new variable for each row...
Ying
... but i am not sure how to do this otherwise. I will try this approach and see if it works.
Ying