views:

125

answers:

3

I am trying to use a snippet of code from a Apple programming guide, and I am getting a EXC_BAD_ACCESS when trying to pass a pointer to a function, right after doing a malloc.

(For Reference: iPhone Application Programming Guide: Event Handling - Listing 3-6)

The code in question is really simple:

CFMutableDictionaryRef touchBeginPoints;
UITouch *touch;

....

CGPoint *point = (CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch);

if (point == NULL)
{
    point = (CGPoint *)malloc(sizeof(CGPoint));
    CFDictionarySetValue(touchBeginPoints, touch, point);
}

Now when the program goes into the if statement it assigns the 'output' of malloc into the point variable/pointer.

Then when it tries to pass point into the CFDictionarySetValue function it crashes the application with: Program received signal: “EXC_BAD_ACCESS”.

Someone suggested not doing the malloc and pass the point var/pointer as: &point, however that still gave me a EXC_BAD_ACCESS.

What I am (and it looks like Apple) doing wrong???

Thanks in advance.

+1  A: 

It's probably crashing because it is trying to retain your CGPoint when you put it in the dictionary except CGPoint isn't a real object but instead a C structure.

Sean
+3  A: 

Sean's answer is mostly correct. According to the documentation for CFDictionarySetValue, it's going to try and retain the value according to how your CFMutableDictionaryRef is set up. I'm guessing that when you create the mutable dictionary (presumably using CFDictionaryCreateMutable()), you're not providing custom callbacks for how to handle setting and removing values.

EDIT:

Another option to providing custom callbacks is to provide NULL for the value callbacks:

CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, NULL);

CGPoint * point = (CGPoint *)malloc(sizeof(CGPoint));
point->x = 42;
point->y = 42;

CFDictionarySetValue(dict, @"foo", point);


CGPoint * newPoint = CFDictionaryGetValue(dict, @"foo");
NSLog(@"%f, %f", newPoint->x, newPoint->y);

Logs:

2010-06-17 11:32:47.942 EmptyFoundation[45294:a0f] 42.000000, 42.000000
Dave DeLong
I didn't do custom call backs... I used the following...touchBeginPoints = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
kdbdallas
@kdbdallas and the documentation for `kCFTypeDictionaryValueCallBacks` says that the default `retain` callback is `CFRetain`, which is where your problem lies. You have to specify custom `retain` and `release` callbacks (and it wouldn't hurt to specify `copy` and `equals` callbacks either), or box the points in `NSValues`, like Dave Dribin suggests.
Dave DeLong
Defining it like this solved it. touchBeginPoints = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, THANKS!
kdbdallas
Doesn't this leak the malloc'ed `CGPoint`? When does it get freed?
Dave Dribin
@Dave yeah, this will leak both the point and the dictionary. I was just writing the code to show that it's possible.
Dave DeLong
While it's easy to avoid leaking the dictionary, it's not easy to avoid leaking the CGPoint. You have to use a custom callback, but even then free() doesn't have the same semantics as CFRelease(). You can't call free() more than once, for example.
Dave Dribin
+2  A: 

CGPoint is a struct, not an Objective-C/CF object, so you need to wrap it in an NSValue:

+ (NSValue *)valueWithPoint:(NSPoint)aPoint

http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSValue_Class/Reference/Reference.html#//apple_ref/occ/clm/NSValue/valueWithPoint:

Dave Dribin
This would solve the problem, but the listing he links to in the question shows the `CGPoint*` getting added directly to the dictionary, without boxing it. Unfortunately, the listing also fails to show how the `CFMutableDictionaryRef` was created.
Dave DeLong
Yeah, pretty sure that listing is completely wrong. There's no way to store a CG/NSPoint directly in a CF/NSDictionary.
Dave Dribin
@Dave Dribin you can store points in a `CFDictionary` just fine. Look at the example in my post. You just have to tell the dictionary to not try to retain the values (which is easily accomplished in the CFDictionary creation functions).
Dave DeLong
Yea it works as long as you setup the CFMutableDictionary correctly. Apple left that part out, and how I created it was wrong
kdbdallas
@Dave DeLong. Sorry, you are correct for CFDictionary. Seems like more trouble than it's worth. I'd just us an `NSMutableDictionary` and `NSValues`.
Dave Dribin
@Dave I probably would too, but I can imagine there are valid use-cases for wanting to avoid the overhead of boxing the points.
Dave DeLong