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:
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
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?)
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
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)
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!
User contributions licensed under CC BY-SA 3.0