Can’t buy reviews like this!

theappleblogcom20080326budgetorganizeanddeclutteryourlifewithreceiptwallet-clipped.pngA few friends of mine pointed me to a ReceiptWallet review today over at The Apple Blog. I had no idea that a review was being written and that it would be so glowing! While the user base of ReceiptWallet is increasing, I usually don’t hear from users unless they are having problems. Reading reviews like this makes me smile and know that all my hard work is appreciated.

Any other reviews out there? Let me know.

Buying a computer? Wait a week?

I’m not sure where I heard it, but I love the saying that says if you want to buy a computer, wait a week. There will never be a good time to buy technology as it will always get “better” and become cheaper. I had been planning on purchasing a new MacBook Pro after I paid my taxes, my my dog’s root canal put that on hold. Today, I’m sort of glad I waited and will be waiting for the new round of MacBook Pros to come out (whenever that is) because Fujitsu announced a 320 GB 7200 RPM portable drives. I always get my portable machines with 7200 RPM drives as I like the extra speed for development. As of now, the largest you can get (at least from Apple in a build to order configuration) is 200 GB for a 7200 RPM drive. Hopefully Apple makes the Fujitsu an option or other vendors increase capacity for the 7200 RPM drives.

Waiting, however, will be hard as my current machine is approaching 2 years old which is an eternity in computer time.

Another PDFKit bug

In my ongoing saga with PDFKit, a user had a crash that I couldn’t reproduce and generously sent me the PDF that caused the issue. I was immediately able to reproduce the problem and furthermore, managed to get Preview to crash on that PDF. The crash was occurring when I tried to generate the thumbnail for the PDF.

Here’s what doesn’t work on all PDFs:

PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:fullPath]];
int thumbnailPage = [[self valueForKey:@"thumbnailpage"] intValue];
int pdfPageCount = [pdfDoc pageCount];
if (thumbnailPage < pdfPageCount && pdfPageCount > 0 && thumbnailPage >= 0)
{
	PDFPage *page = [pdfDoc pageAtIndex:thumbnailPage];
	if (page != nil)
	{
		NSData *data = [page dataRepresentation];
		if (data)
		{
			image = [[NSImage alloc] initWithData:data];
		}
	}
}

[pdfDoc release];

Here’s what works on all PDFs I’ve tested including the one in question:

NSPDFImageRep *pdfRep = [[NSPDFImageRep alloc] initWithData:
	[NSData dataWithContentsOfURL:[NSURL fileURLWithPath:fullPath]]];
if (pdfRep)
{
	int thumbnailPage = [[self valueForKey:@"thumbnailpage"] intValue];
	int pdfPageCount = [pdfRep pageCount];
	if (thumbnailPage < pdfPageCount && pdfPageCount >= 0 && thumbnailPage >= 0)
	{
		_DebugLog(@"thumbnail page count is valid");
		[pdfRep setCurrentPage:thumbnailPage];
		image = [[NSImage alloc] initWithSize:[pdfRep bounds].size];
		[image addRepresentation:pdfRep];
		[image setDataRetained:YES];
	}
	[pdfRep release];
}

So what does this tell me? It tells me that NSPDFImageRep handles PDFs better than PDFDocument/PDFPage does. I fixed that crash and optimized some other code while I was at it. This still doesn’t solve all my problems with PDFKit as the PDF in question crashes when I try to write metadata to it (it crashes Preview as well). Not much I can do about that, yet.

Update:Funny, when I put this code in a test application, it no longer crashes. I still believe something is wrong with PDFKit as the crash looks very suspicious:

Thread 0 Crashed:
0   libSystem.B.dylib             	0x93287474
tiny_malloc_from_free_list +
548
1   libSystem.B.dylib             	0x93280ba8 szone_malloc + 200
2   com.apple.CoreFoundation      	0x92342414 __CFDictionaryGrow + 200
3   com.apple.CoreFoundation      	0x92342d64 CFDictionarySetValue + 252
4   libCGATS.A.dylib              	0x95265440
get_name_with_name_code_nl + 532
5   libCGATS.A.dylib              	0x952654cc
ats_name_handler_copy_full_name + 20
6   libCGATS.A.dylib              	0x95266ad0 copy_full_name + 36
7   com.apple.CoreGraphics        	0x919b1fd0 CGFontNameTableCreate +
252
8   com.apple.CoreGraphics        	0x919b1e9c CGFontGetPostScriptName
+ 36
9   libPDFRIP.A.dylib             	0x068fba64
PDFFontType1EmitDefinition + 60
10  libPDFRIP.A.dylib             	0x068f8678 PDFFontEmitDefinitions +
52
11  libPDFRIP.A.dylib             	0x068f9aa8 emitFontDefinition + 20
12  com.apple.CoreFoundation      	0x923743e0 CFSetApplyFunction + 284
13  libPDFRIP.A.dylib             	0x068f9b0c
PDFFontSetEmitDefinitions + 68
14  libPDFRIP.A.dylib
        	0x068f68e0 PDFDocumentFinalize + 312
15  libPDFRIP.A.dylib             	0x068f5314 pdf_Finalize + 24
16  com.apple.CoreGraphics        	0x919e5914
CGContextDelegateFinalize + 72
17  com.apple.CoreFoundation      	0x92371840 _CFRelease + 216
18  com.apple.CoreGraphics        	0x919e58f4
CGContextDelegateFinalize + 40
19  com.apple.PDFKit              	0x914f77d4 -
[PDFPage(PDFPageInternal) writeToConsumer:] + 444
20  com.apple.PDFKit              	0x914f6a28 -[PDFPage
dataRepresentation] + 84
21  ...gtenterprises.receiptwallet	0x0001b8dc -[ReceiptMO
imageWithWritingThumbnail:] + 988

Defective dog

Our dog had his root canal yesterday performed by Dr. Brook Niemiec. Everything went as planned, except after the initial exam, the vet showed us that Marley had an extra, useless tooth that was encroaching on his front teeth. So, that tooth had to be removed which made the $2000 dog bone even more expensive. Other than Marley being on some drugs for a week and having to eat soft food for 2 weeks, he seems to be doing fine and will be back to his normal self soon. As Marley has been a part of our lives for 3 years, I was very worried about the procedure as he had to be anesthetized. This may be a routine procedure, but anesthesia scares me. I’ve asked for copies of the X-rays and once I get them, I’ll put them up. I figure that for what I paid to repair Marley’s teeth, I might as well let others look at the pictures.

Threading, a necessary evil

Anyone I know that really has a clue avoids multi-threading programming (except for some server applications) as there are so many gotchas. Making things thread safe sounds easy, but is extremely hard as it is quite easy to overlook an item or two I got bit by this in ReceiptWallet in 2 spots. In one case, I build thumbnails on a separate thread to keep the main thread (where the user interacts) running. The problem is that if the user does something, i.e. remove a page or change metadata, I clear the underlying document in the main thread. So the secondary thread tries to use the document that no longer exists and quickly crashes. The fix wasn’t difficult; I simply had to know when the secondary thread was running and don’t change things on the main thread until the secondary thread exited. Of course, I could never reproduce this (like many thread related bugs, it is nearly impossible to track down the cause), so figuring this out was a bit problematic.

The second thread related issue was a little easier to track down, but a bit less straightforward that I messed up. In this case, I used something like:

	[self performSelector:@selector(saveChanges) withObject:nil afterDelay:0.0];

and then proceeded to do:

	NSTask *mdImportTask = [[NSTask alloc] init];
	[mdImportTask setLaunchPath:@"/usr/bin/mdimport"];
	[mdImportTask setArguments:[NSArray arrayWithObject:path]];
	[mdImportTask launch];
	[mdImportTask waitUntilExit];
	[mdImportTask release];

The problem here is that the waitUntilExit runs the main loop until the task (which is actually a separate thread) completes. So saveChanges actually fires when the task is running. The task is in a function that can get called by saveChanges so things become messy quite fast. I had to re-work some code to fix this.

I worked on a project awhile ago that was heavily threaded and despite reassurances that the code was thread safe, it was a royal mess to track down bugs. While I use threads in a number applications I write, I write to avoid them as much as possible, except in cases where doing stuff on the main thread would make the UI unresponsive.