Quirks, bugs with TahDoodle


#1

While playing with deleting/inserting items, and resizing the window, I noticed one quirk and triggered an exception.

The quirk: if I resized a blank list to just the right size, I could see “Your document contents here” appear above the combined scroll/table view. Looking through the .xib file, I found that string associated with a TextFieldCell object; is there something to be done to avoid that?

The exception that I now see when hitting the “Insert” button seems odd; I definitely have a createNewItem: method. This appears consistently when playing with the original test document. Creating a new blank document does not result in an exception, even though the (empty array) .tdl files are the same.

(Correction: after quitting and re-launching a couple of times, I can now get this to throw an exception for the new document.)

2011-11-26 22:32:40.602 TahDoodle[7492:407] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100215e20
2011-11-26 22:32:40.603 TahDoodle[7492:407] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100215e20
2011-11-26 22:32:40.658 TahDoodle[7492:407] (
	0   CoreFoundation                      0x00007fff8de16286 __exceptionPreprocess + 198
	1   libobjc.A.dylib                     0x00007fff8943cd5e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8dea24ce -[NSObject doesNotRecognizeSelector:] + 190
	3   CoreFoundation                      0x00007fff8de03133 ___forwarding___ + 371
	4   CoreFoundation                      0x00007fff8de02f48 _CF_forwarding_prep_0 + 232
	5   TahDoodle                           0x00000001000012f4 -[BNRDocument createNewItem:] + 164
	6   CoreFoundation                      0x00007fff8de05a1d -[NSObject performSelector:withObject:] + 61
	7   AppKit                              0x00007fff8591d710 -[NSApplication sendAction:to:from:] + 139
	8   AppKit                              0x00007fff8591d642 -[NSControl sendAction:to:] + 88
	9   AppKit                              0x00007fff8591d56d -[NSCell _sendActionFrom:] + 137
	10  AppKit                              0x00007fff8591ca30 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2014
	11  AppKit                              0x00007fff8599c8e0 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 489
	12  AppKit                              0x00007fff8591b63a -[NSControl mouseDown:] + 786
	13  AppKit                              0x00007fff858e60e0 -[NSWindow sendEvent:] + 6306
	14  AppKit                              0x00007fff8587e68f -[NSApplication sendEvent:] + 5593
	15  AppKit                              0x00007fff85814682 -[NSApplication run] + 555
	16  AppKit                              0x00007fff85a9380c NSApplicationMain + 867
	17  TahDoodle                           0x0000000100001172 main + 34
	18  TahDoodle                           0x0000000100001144 start + 52

At any rate, I’ll use this as an opportunity to play with debugging to see if I can sort out what happened.

iTunes 4.2.1, Mac OS X 10.7.2, no updates pending.

Thanks, Aaron, for a fantastic book. I have a pile of older books, and I’m a reasonably experienced programmer (but not in GUI environments), and Xcode 4 + ARC had me desperate for something more up to date. Your book definitely is a good springboard. One suggestion I’d make for future editions is to find a way to visually highlight the content in the early chapters that a C programmer won’t know.


#2

In case I blew the delete code to somehow provoke the exception…

- (IBAction)deleteItem:(id)sender
{
    NSInteger row = [itemTableView selectedRow];
    if (row < 0) {
        // Button was hit with no row selected
        return;
    }
    
    assert(row < [todoItems count]); // Failure means either the GUI or my data set is out of sync
    [todoItems removeObjectAtIndex:row];
    [itemTableView reloadData];
    [self updateChangeCount:NSChangeDone];
}

#3

Got it. When pulling data from the p-list file, todoItems is being created as an NSArray, not a mutable array, so the runtime is throwing an exception at addObject:


#4

It looks like the code from the book is incorrect.

In dataOfType, dataWithPropertyList options is specified as NSPropertyListMutableContainers, but per the organizer it should be set to 0, so that’s incorrect but irrelevant.

In readFromData, the options argument to propertyListWithData should be NSPropertyListMutableContainersAndLeaves.


#5

Iiiiinteresting! As to the technical error - you’re right. The “0” and “NSPropertyListMutableContainers” is switched between the two methods - our apologies! Nice catch~ this has now been fixed for future printings.

The correct code for these two methods should be:

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
    if (!toDoItems) {
        toDoItems = [NSMutableArray array];
    }
    
    NSData *data = [NSPropertyListSerialization dataWithPropertyList:toDoItems 
                                                              format:NSPropertyListXMLFormat_v1_0 
                                                             options:0 //Was NSPropertyListMutableContainers
                                                               error];
    return data;
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    toDoItems = [NSPropertyListSerialization propertyListWithData:data 
                                                          options:NSPropertyListMutableContainers //Was 0
                                                           format:NULL 
                                                            error];
    return (toDoItems != nil);
}

The “0” mistakenly used in the propertyListWithData:options:format:error: method maps to “NSPropertyListImmutable” in the NSPropertyListMutablilityOptions provided by Apple:

enum {
   NSPropertyListImmutable = kCFPropertyListImmutable,
   NSPropertyListMutableContainers = kCFPropertyListMutableContainers,
   NSPropertyListMutableContainersAndLeaves = kCFPropertyListMutableContainersAndLeaves
};
typedef NSUInteger NSPropertyListMutabilityOptions;

// and...

enum CFPropertyListMutabilityOptions {
   kCFPropertyListImmutable = 0,
   kCFPropertyListMutableContainers = 1,
   kCFPropertyListMutableContainersAndLeaves = 2
};

We do indeed, when reading the plist, want to use NSPropertyListMutableContainers instead of NSPropertyListImmutable. We don’t need NSPropertyListMutableContainersAndLeaves (although it’ll also work), because while we need an NSMutableArray to be thawed from the plist, but we don’t really care about the mutability of the strings in the array. They are simply replaced in the array when edited anyway.

Here’s the interesting bit - I’m not actually able to duplicate the exception. If I log the class of the toDoItems array immediately after thawing it from the plist with propertyListWithData::::, I have an NSMutableArray regardless of the option passed in the options: argument, which explains why we didn’t catch this error. So that I can understand the source of this interesting behavior, I’d like to compare setups.

Would you be willing to send me the CPU architecture, specific Mac OS version, and specific Xcode version that you’re using? Also, what is the deployment target of your project?

Thanks,
~Mikey


#6

Sounds like a bug worth submitting to Apple. Odd.

10.7.2 (11C74), 2.66 GHz Intel Core 2 Duo (Early 2009 iMac), Xcode 4.2.1 (4D502), deployment target is 10.7.

…Ah, I believe I’ve identified the discrepancy.

Try saving and opening an empty list.


#7

I didn’t hit the exception either with the transposed options. I also just emptied my list and was able to save it empty and load it empty without any issues. :question:


#8

I have no further theories, then; that seemed to do it for me. I used NSLog(@"%@", [todoItems class]) to verify that it was thawed immutably, and I only saw a mutable array when restoring a non-empty array.

macshome, can you verify that your data file looks like the following?

[code]<?xml version="1.0" encoding="UTF-8"?>

[/code]

#9

Yeah, this one is obvious in retrospect, didn’t delete the default text field in my .xib file before throwing views at it.


#10

[quote=“macintux”]I have no further theories, then; that seemed to do it for me. I used NSLog(@"%@", [todoItems class]) to verify that it was thawed immutably, and I only saw a mutable array when restoring a non-empty array.

macshome, can you verify that your data file looks like the following?

[code]<?xml version="1.0" encoding="UTF-8"?>

[/code][/quote]

Here is the text of my empty list data file…

[code]<?xml version="1.0" encoding="UTF-8"?>

[/code]

#11

Mikey, I’m a newcomer to Objective C but (aside from the weird fact that the issue is not reproducible on your system) aren’t you actually logging the class declared at compile-time (which is an NSMutableArray*) rather than the class instance available at runtime (which in the erroneous version of the code was NSArray*)?