small medium large xlarge

30 Apr 2010, 17:06
John Franco (1 post)

On page 253 in BMP I noticed the editing process sends messages to NSDictionary where the creation of the NSArray set up NSMutableDictionary objects. Is there a particular reason for this? Could the editing method have sent its messages to a mutable dictionary as well?

18 Jul 2011, 19:28
Paul Hancock (3 posts)

Any reply? I was wondering the same thing. The class factory method creates an autoreleased NSMutableDictionary. However, it is assigned to a pointer to an (immutable) NSDictionary object (e.g. milkItem). Subsequently, a setValue:forKey: message is sent to the milkItem object (the setValue:forKey: message only makes sense for a mutable object). Why is milkItem defined as a pointer to an NSDictionary object and then made to reference a mutable object? NSMutableDictionary inherits from NSDictionary. I would have guessed that the immutable reference to a mutable object would only de-reference (give access to) the immutable methods. Obviously, I’m wrong. Would like to understand why.

Here is the code

  • (id)init { self = [super init]; if (self) { shoppingListArray = [[NSMutableArray alloc] init];

      // Add the milk item
      NSDictionary *milkItem = [NSMutableDictionary dictionary];
      [milkItem setValue:@"Milk" forKey:@"itemNameKey"];
      [milkItem setValue:[NSNumber numberWithInt:3] forKey:@"itemQuantityKey"];
      [shoppingListArray addObject:milkItem];

    } return self; }

19 Jul 2011, 19:04
Tim Isted (105 posts)

Wow - I’m so sorry for not having picked this up before.

The Short Version This is a mistake in the code - the pointers should indeed be NSMutableDictionary rather than NSDictionary as you are using setValue:forKey:.

The Longer Version

Hopefully this answers your question, Paul?

A pointer is just a value representing the address of something in memory. It doesn’t make any difference at run-time whether you declare it as an NSObject, NSWindow, or NSMutableDictionary. It does make a difference at compile-time, however.

The compiler uses the type of the pointer to help you out by checking whether it thinks a certain object will respond to a certain message. For example, if you try to compile this:

    NSObject *someObject = [[NSObject alloc] init];
    [someObject showWindow:self];
    [someObject release];

you’ll get a compiler warning that 'someObject' may not respond to 'showWindow:'.

Note the words “may not respond” rather than “doesn’t respond”; this is only the compiler’s best guess. In this case, it happens to be right, and this code would crash at runtime.

If you try compiling this, however:

   NSWindowController *someObject = [[NSObject alloc] init];
   [someObject showWindow:self];
   [someObject release];

you’ll lose the warning around showWindow: as the compiler now considers someObject as an NSWindowController, which does respond to showWindow:. This code will, however, still crash at runtime because although you declared the pointer as an NSWindowController, you assigned it to point to an NSObject. NSObject doesn’t respond to showWindow:, so the application will crash.

Depending on build settings, you may or may not see a compiler warning around the assignment line.

To return to the mistake in the book’s code, you’re doing this:

   NSDictionary *itemDictionary = [shoppingListArray objectAtIndex:rowIndex];
   [itemDictionary setValue:blah forKey:blah];

Under any other circumstance, the compiler would generate a warning here that 'itemDictionary' may not respond to 'setValue:forKey:'. But, and this is where it gets confusing, because it’s the setValue:forKey: method, which happens to be the same method as often used for Key-Value-Coding, the compiler is quite happy.

NSObject is actually defined as being able to respond to the setValue:forKey: method (try using Xcode’s File > Open Quickly… and opening NSKeyValueCoding.h). The compiler thinks you’re using the standard KVC method, setValue:forKey:, but at runtime your NSMutableDictionary object will respond to this method with its own implementation.

So, I’m sorry I didn’t notice this mistake in the code earlier. It doesn’t crash, or generate warnings, but it is incorrect nonetheless.

A quick intro to KVC, if an object has a property someProperty, you can either access that property through accessor methods, like this:

   id somePropertyValue = [someObject someProperty];
   [someObject setSomePropertyValue:somethingElse];

or (and for this to work you must have followed the capitalization conventions mentioned in the book), you can use KVC methods to refer to the property using NSStrings:

   id somePropertyValue = [someObject valueForKey:@"someProperty"];
   [someObject setValue:somethingElse forKey:@"someProperty"];

These two examples are identical, provided certain KVC requirements are met. The setValue:forKey: method will raise an exception if you try to set a value on a property that isn’t “KVC-compliant,” i.e. that doesn’t have a suitable setProperty: method defined.

23 Jul 2011, 23:10
Paul Hancock (3 posts)

Thanks for the response. I guess since NSObject responds to setValue:forKey, the compiler was fooled. At run-time, the appropriate method got called anyway. I really appreciate your book. It is well written and the progression of the material flows nicely.

25 Aug 2011, 21:17
Mdeh (7 posts)

My book, on p253 shows the method to edit the tableview. The dictionary is retrieved as a “non-mutable” NSDictionary, but this does not seem to effect the behavior of the application. I suspect though, one should be working with an NSMutableDictionary?