-
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]; } } }
-
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.
-
Cocoa bindings causing dealloc to not be called?
In tracking down a bug in ReceiptWallet, I discovered some end (at least to me) odd behavior. ReceiptWallet is an NSDocument based application. When the document is closed, is should call dealloc to release its memory. This wasn't happening. It appears that some things in my code prevented dealloc from being called; I had to unbind some Cocoa bindings (not all of them, however):
[_receiptsArrayController unbind:@"selectionIndexes"]; [photoSizeSlider unbind:@"value"]; [self unbind:@"currentSelectedIndexes"];
and then I had to remove some observers:
[collectionsTreeController removeObserver:self forKeyPath:@"selectionIndexPaths"]; [userDefaultsController removeObserver:self forKeyPath:@"values.Show Details With Thumbnails"];
and to top it off, I had to set the view on a toolbar item (a search item) to nil:
extern NSString *SearchToolbarItemIdentifier; NSArray *items = [[[self window] toolbar] items]; NSEnumerator *itemEnumr = [items objectEnumerator]; NSToolbarItem *item = nil; while (item = [itemEnumr nextObject]) { if ([[item itemIdentifier] isEqualToString:SearchToolbarItemIdentifier]) { [item setView:nil]; break; } }
I can understand removing the observer for user defaults, but the bindings have me confused as the bindings are for items that should get released anyway. I'm probably missing something simple, but I'm glad I figured this one out.