EXC_BAD_ACCESS in CFAttributedStringSetAttribute and NSNumber?

3

I am getting an infuriating EXC_BAD_ACCESS error in an objective c app I am working on. Any help you could offer would be much appreciated. I have tried the normal debug methods for this error (turning on NSZombieEnabled, checking retain/release/autorelease to make sure I'm not trying to access a deallocated object, etc.) and it hasn't seemed to help.

Basically, the error always occurs in this function:

void op_TJ(CGPDFScannerRef scanner, void *info)
{
    PDFPage *self = info;
    CGPDFArrayRef array;

    NSMutableString *tempString = [NSMutableString stringWithCapacity:1];
    NSMutableArray *kernArray = [[NSMutableArray alloc] initWithCapacity:1];

    if(!CGPDFScannerPopArray(scanner, &array)) {
        [kernArray release];
        return;
    }

    for(size_t n = 0; n < CGPDFArrayGetCount(array); n += 2)
    {
        if(n >= CGPDFArrayGetCount(array))
            continue;

        CGPDFStringRef pdfString;

        // if we get a PDF string
        if (CGPDFArrayGetString(array, n, &pdfString))
        {
            //get the actual string
            const unsigned char *charstring = CGPDFStringGetBytePtr(pdfString);

            //add this string to our temp string
            [tempString appendString:[NSString stringWithCString:(const    char*)charstring encoding:[self pageEncoding]]];
            //NSLog(@"string: %@", tempString);

            //get the space after this string
            CGPDFReal r = 0;
            if (n+1 < CGPDFArrayGetCount(array)) {
                CGPDFArrayGetNumber(array, n+1, &r);

                // multiply by the font size
                CGFloat k = r;
                k = -k/1000 * self.tmatrix.a * self.fontSize;


                CGFloat kKern = self.kern * self.tmatrix.a;
                k = k + kKern;

                // add the location and kern to the array
                NSNumber *tempKern = [NSNumber numberWithFloat:k];
                NSLog(@"tempKern address: %p", tempKern);
                [kernArray addObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:[tempString length] - 1], tempKern, nil]];

            }
        }
    }

    // create an attribute string
    CFMutableAttributedStringRef attString =    CFAttributedStringCreateMutable(kCFAllocatorDefault, 10);

    CFAttributedStringReplaceString(attString, CFRangeMake(0, 0), (CFStringRef)tempString);

    //apply overall kerning
    NSNumber *tkern = [NSNumber numberWithFloat:self.kern * self.tmatrix.a * self.fontSize];
    CFAttributedStringSetAttribute(attString, CFRangeMake(0, CFAttributedStringGetLength(attString)), kCTKernAttributeName, (CFNumberRef)tkern);

    //apply individual kern attributes
    for (NSArray *kernLoc in kernArray) {
        NSLog(@"kern location: %i, %i", [[kernLoc objectAtIndex:0] intValue],[[kernLoc objectAtIndex:1] floatValue]);
        CFAttributedStringSetAttribute(attString, CFRangeMake([[kernLoc objectAtIndex:0] intValue], 1), kCTKernAttributeName, (CFNumberRef)[kernLoc objectAtIndex:1]);
    }

    CFAttributedStringReplaceAttributedString([self cfAttString], CFRangeMake(CFAttributedStringGetLength([self cfAttString]), 0), attString);

    //release
    CFRelease(attString);
    [kernArray release];


}

The program always crashes because of line

CFAttributedStringSetAttribute(attString, CFRangeMake([[kernLoc objectAtIndex:0] intValue], 1), kCTKernAttributeName, (CFNumberRef)[kernLoc objectAtIndex:1])

And it seems to depend on a few things:

  1. if [kernLoc objectAtIndex:1] refers to an [NSNumber numberWithFloat:k] where k = 0 (in other words, if k = 0 above where I populate kernArray) then the program crashes almost immediately

  2. If I comment out the line k = k + kKern, it takes longer for the program to crash, but does eventually (why would the crash depend on this value?)

  3. If I change the length of CFRangeMake from 1 to 0, it takes a lot longer for the program to crash, but still eventually does. (I don't think I am trying to access beyond the bounds of attString, but am I missing something?)

When it crashes, I get something similar to:

#0  0x942c7ed7 in objc_msgSend ()
#1  0x00000013 in ?? ()
#2  0x0285b827 in CFAttributedStringSetAttribute ()
#3  0x0000568f in op_TJ (scanner=0x472a590, info=0x4a32320) at /Users/Richard/Desktop/AppTest/PDFHighlight 2/PDFScannerOperators.m:251

Any ideas? It seems like somewhere along the way I am overwriting memory or trying to access memory that has been changed, but I have no idea. If there's anymore information I can provide, please let me know.

Thanks, Richard

iphone
objective-c
exc-bad-access
asked on Stack Overflow Jun 6, 2010 by RichardR • edited Jun 6, 2010 by dreamlax

2 Answers

4

Your crash looks like the result of an over release somewhere. It's difficult to track down what the issue might be, particularly as you are mixing core foundation with Cocoa. The memory management rules are different for core foundation.

I think I would pull all of the references to data out of CFAttributedStringSetAttribute so that I could NSLog them or inspect them with the debugger, like this:

NSNumber* rangeStart = [kernLoc objectAtIndex:0];      // Debug: make sure it is a number
NSNumber attrValue = [kernLoc objectAtIndex:1];        // Debug: make sure it is a number
CFRange range = CFRangeMake([rangeStart intValue], 1); // Debug: make sure it is a valid range for the string
CFAttributedStringSetAttribute(attString, range, kCTKernAttributeName, (CFNumberRef)attrValue)
answered on Stack Overflow Jun 7, 2010 by JeremyP
2

Follow up - it seems like CFAttributedStringSetAttributes (plural) is the way to go. I still need to implement it into my program, but in a small test, this function allows me to overwrite previously defined values without getting an EXC_BAD_ACCESS crash. Would have been nice if this was documented somewhere. Hope this helps other people out there. Thanks!

answered on Stack Overflow Jun 8, 2010 by RichardR

User contributions licensed under CC BY-SA 3.0