Bindings Problem
-
I'm sure what I'm trying to do is not that difficult but I've been pulling my hair out for a while now on this problem.
I have an application whose model could be updated without the user directly doing anything. So bindings seems like the perfect fit b/c the interface will reflect these sorts of changes when they occur. But I can't seem to get updates in my model to appear in the interface. My model is an NSMutableSet that contains NSMutableDictionaries. I then have a NSTableView with several columns. I want each column in the table to display a value from each NSMutableDictionary.
@interface MyController : NSObject
{
NSMutableSet *myset;
}
//I initialize the NSMutableSet in this method.
- (void)awakeFromNib;
//Setter and Getter to ensure KVC compliance.
- (void)setMyset:(NSMutableSet *)set;
- (NSMutableSet *)myset;
//This method is here for testing. A button in the UI is connected to the method.
//When executed this method creates a new NSMutableDictionary. Adds some key/value
//pairs to it then adds the NSMutableDictionary to the NSMutableSet (myset).
- (IBAction)addValueToSet:(id)sender;
In Interface Builder I have dragged out an NSObject and set its type to be "MyController". I then dragged out an NSObjectController and set its "Content" connection to the MyController object. I then dragged out an NSArrayController and bound its Content Array binding to the Object Controller with a Controller Key of "selection" and a Model Key Path of "myset". I also set the NSArrayController's Class Name to NSMutableDictionary since that is what is held by my NSMutableSet. I setup a NSTableView in my application's window and then bound its first table column's "Value" binding to the NSArrayController using "arrangedObjects" as the Controller Key and "Caption" as the Model Key Path. "Caption" is the key to a key/value pair in the NSMutableDictionary that is held by the NSMutableSet (myset).
In the addValueToSet: method I have the following code:
- (IBAction)addValueToSet:(id)sender
{
NSString *newString = @"New Value";
NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:1] autorelease];
[dict setValue:newString forKey:@"Caption"];
[myset addObject:dict];
}
When I click the button in my interface nothing happens. I know the addValueToSet: method is being called however I would expect the interface to update based upon my updating the model. Can anybody tell me what I'm doing wrong? -
On Jan 14, 2010, at 3:11 pm, Carter R. Harrison wrote:
> In the addValueToSet: method I have the following code:See <http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Cocoa
> - (IBAction)addValueToSet:(id)sender
>
Bindings/Concepts/Troubleshooting.html#//apple_ref/doc/uid/TP40002148>
mmalc -
In addition to what mmalc pointed you to, there are other red flags in your description:
On Jan 14, 2010, at 5:11 PM, Carter R. Harrison wrote:
> My model is an NSMutableSet that contains NSMutableDictionaries.
I think this is asking for trouble. A set of mutable dictionaries doesn't make much sense.
One of the defining characteristics of a set is that it can't have two equal objects in it at once. However, mutable dictionaries can change whether they are equal when you mutate them. "Equal" is determined by the -isEqual: and -hash methods, not by identity (memory location).
Sets are not implemented to a) be aware of when the contained objects change or b) cope with the change.
Quite aside from the question of whether two dictionaries become equal after being added to the set is the fact that they will change their hash value when mutated. While the implementation of NSSet is private, it almost certainly depends on the hash value of the contained objects. So, if you put a dictionary into the set, mutate it, and then ask the set if it contains the dictionary, it may very well say that it does not. That's because its internal bookkeeping used the original hash to file the dictionary in its data structures, but uses the new hash to try to look it up.
The NSSet documentation says:
"Note that if mutable objects are stored in a set, either the hash method of the objects shouldnât depend on the internal state of the mutable objects or the mutable objects shouldnât be modified while theyâre in the set (note that it can be difficult to know whether or not a given object is in a collection)."
A similar warning in the description of -[NSObject hash] specifically uses a mutable dictionary as an example of a mutable object which may change its hash when mutated.
I recommend using immutable dictionaries or instances of a custom class with proper properties and with semantics for -isEqual: and -hash that aren't affected by modifications to those properties. The default semantics provided by NSObject, which is that equality is the same as object identity, are appropriate in the most common circumstances. (It depends on whether your custom class is a "value object" or not.)
> I then have a NSTableView with several columns. I want each column in the table to display a value from each NSMutableDictionary.
NSArrayController is only documented as handling sets in the case when managing a relationship of a managed object. I have no idea if it works in the general case. I wouldn't bet on it.
Since a table view is an inherently ordered construct, I'd strongly recommend that you use an indexed collection (e.g. array) for your model.
> //This method is here for testing. A button in the UI is connected to the method.
> //When executed this method creates a new NSMutableDictionary. Adds some key/value
> //pairs to it then adds the NSMutableDictionary to the NSMutableSet (myset).
> - (IBAction)addValueToSet:(id)sender;
> - (IBAction)addValueToSet:(id)sender
> {
> NSString *newString = @"New Value";
> NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:1] autorelease];
> [dict setValue:newString forKey:@"Caption"];
> [myset addObject:dict];
> }
This illustrates my concern about using a set. In addition to the fact (alluded to by mmalc) that you're not mutating the set in a KVO-compliant manner, all of the dictionaries added by this method are equal. Therefore, there's no reason to believe that myset is actually being changed by the -addObject: message. If it already contains an equivalent dictionary, then -addObject: would do nothing.
Regards,
Ken -
> On Sat, 2010/01/16, Ken Thomases <ken...> wrote:
>> On 2010 Jan 14, at 17:11, Carter R. Harrison wrote:
>> My model is an NSMutableSet that contains
>> NSMutableDictionaries.
>
> I think this is asking for trouble. A set of mutable
> dictionaries doesn't make much sense.
>
> ..."Equal" is determined by the -isEqual: and -hash
> methods, not by identity (memory location).
So, it's quite possible that he has a "hash" used by
the NSMutableSet which doesn't depend on the contents
of the mutable dictionary once it's been created.
Still, though I can imagine someone wanting such a
set-up, I have to wonder what purpose it serves,
as opposed, e.g.,
to a mutable dictionary of dictionaries, or
a mutable array of dictionaries. -
On Jan 17, 2010, at 11:15 AM, Jeffrey Oleander wrote:
>> On Sat, 2010/01/16, Ken Thomases <ken...> wrote:
>>> On 2010 Jan 14, at 17:11, Carter R. Harrison wrote:
>>> My model is an NSMutableSet that contains
>>> NSMutableDictionaries.
>>
>> I think this is asking for trouble. A set of mutable
>> dictionaries doesn't make much sense.
>>
>> ..."Equal" is determined by the -isEqual: and -hash
>> methods, not by identity (memory location).
>
> So, it's quite possible that he has a "hash" used by
> the NSMutableSet which doesn't depend on the contents
> of the mutable dictionary once it's been created.
Huh? The hash in question is that of the mutable dictionaries. He doesn't get to decide this. It's a near certainty that the hash of a dictionary is sensitive to its contents. First, logically, how else would the NSDictionary implement equality? Second, the documentation I cited virtually asserted that it's so.
So, if you mutate a dictionary while it's inside a collection, that will cause trouble. The documentation I cited says this, too, without equivocation.
He also doesn't get to decide on the hash of the set, if that's what you were thinking about.
He could implement some custom class and decide how its hash is implemented, but that's beside the point I was making. In fact, that was one of my suggested solutions.
In other words, I'm not sure what you're trying to say.
Regards,
Ken -
> On Sun, 2010/01/17, Ken Thomases <ken...> wrote:
>> On 2010 Jan 17, at 11:15, Jeffrey Oleander wrote:
>>> On Sat, 2010/01/16, Ken Thomases <ken...>
> wrote:
>>>> On 2010 Jan 14, at 17:11, Carter R. Harrison wrote:
>>>> My model is an NSMutableSet that contains
>>>> NSMutableDictionaries.
>>>
>>> I think this is asking for trouble. A set of
>>> mutable dictionaries doesn't make much sense.
>>>
>>> ..."Equal" is determined by the -isEqual: and
>>> -hash methods, not by identity (memory location).
>> So, it's quite possible that he has a "hash" used by
>> the NSMutableSet which doesn't depend on the contents
>> of the mutable dictionary once it's been created.
>
> Huh? The hash in question is that of the mutable
> dictionaries. He doesn't get to decide this.Â
> It's a near certainty that the hash of a dictionary is
> sensitive to its contents. First, logically, how else
> would the NSDictionary implement equality? Second, the
> documentation I cited virtually asserted that it's so.
>
> So, if you mutate a dictionary while it's inside a
> collection, that will cause trouble. The documentation
> I cited says this, too, without equivocation.
>
> He also doesn't get to decide on the hash of the set, if
> that's what you were thinking about.
>
> He could implement some custom class and decide how its
> hash is implemented, but that's beside the point I was
> making. In fact, that was one of my suggested
> solutions.
>
> In other words, I'm not sure what you're trying to say.
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Colle
ctions/Articles/Sets.html
"Set Fundamentals: Note that if mutable objects are stored in a set, either the hash method of the objects shouldnât depend on the internal state of the mutable objects or the mutable objects shouldnât be modified while theyâre in the set (note that it can be difficult to know whether or not a given object is in a collection)."
So, you can use your own hash function to determine where in the set each item is stored. In this case, each item stored in the set is an NSMutableDictionary.
http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Founda
tion/Classes/NSDictionary_Class/Reference/Reference.html
"Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key. However, the methods defined in this cluster insulate you from the complexities of working with hash tables, hashing functions, or the hashed value of keys. The methods described below take keys directly, not their hashed form."
So, you may use your own hash function to create your keys... or not, as you wish. But then the NSDictionary and NSMutableDictionary will hash the keys you pass to their methods using their own, internal, hash function, to determine where objects are stored in the dictionary.
But my primary point was to ask the original poster to think about why he is using this arrangement of instances of NSMutableDictionary stored in an NSMutableSet. -
On Jan 18, 2010, at 11:01 AM, Jeffrey Oleander wrote:
> http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Colle
ctions/Articles/Sets.html
>
> "Set Fundamentals: Note that if mutable objects are stored in a set, either the hash method of the objects shouldnât depend on the internal state of the mutable objects or the mutable objects shouldnât be modified while theyâre in the set (note that it can be difficult to know whether or not a given object is in a collection)."
>
> So, you can use your own hash function to determine where in the set each item is stored. In this case, each item stored in the set is an NSMutableDictionary.
And... you can't change the hash function used by NSMutableDictionary. So, for a custom class, you can implement a custom -hash method. But for NSMutableDictionary, you can't. And, if you do implement a custom -hash method for your custom class which is insensitive to the "contents" (whatever that might be) of its instances, then that's a _very_ different equality semantic than NSDictionary provides.
> http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Founda
tion/Classes/NSDictionary_Class/Reference/Reference.html
>
> "Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key. However, the methods defined in this cluster insulate you from the complexities of working with hash tables, hashing functions, or the hashed value of keys. The methods described below take keys directly, not their hashed form."
>
> So, you may use your own hash function to create your keys... or not, as you wish.
Um, the keys are not typically the output of a hash function. Well, you can use such for keys, but that's irrelevant to the hash of the dictionary object, itself.
> But then the NSDictionary and NSMutableDictionary will hash the keys you pass to their methods using their own, internal, hash function, to determine where objects are stored in the dictionary.
Right. So, if you mutate the dictionary while it's in the set, you will break things. That's why a set of mutable dictionaries (as opposed to immutable dictionaries, or some other data structure entirely) raises a huge red flag.
As I previously cited, from the documentation of -[NSObject hash]:
http://developer.apple.com/mac/library/documentation/cocoa/Reference/Founda
tion/Protocols/NSObject_Protocol/Reference/NSObject.html
"If a mutable object is added to a collection that uses hash values to determine the objectâs position in the collection, the value returned by the hash method of the object must not change while the object is in the collection. Therefore, either the hash method must not rely on any of the objectâs internal state information or you must make sure the objectâs internal state information does not change while the object is in the collection. Thus, for example, a mutable dictionary can be put in a hash table but you must not change it while it is in there. (Note that it can be difficult to know whether or not a given object is in a collection.)"
Note that this case of mutable dictionaries in a collection is explicitly warned about.
> But my primary point was to ask the original poster to think about why he is using this arrangement of instances of NSMutableDictionary stored in an NSMutableSet.
Yes, and that was my point, too.
-Ken -
On Jan 18, 2010, at 4:55 PM, Ken Thomases wrote:
> On Jan 18, 2010, at 11:01 AM, Jeffrey Oleander wrote:
>
>> http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Colle
ctions/Articles/Sets.html
>>
>> "Set Fundamentals: Note that if mutable objects are stored in a set, either the hash method of the objects shouldnât depend on the internal state of the mutable objects or the mutable objects shouldnât be modified while theyâre in the set (note that it can be difficult to know whether or not a given object is in a collection)."
>>
>> So, you can use your own hash function to determine where in the set each item is stored. In this case, each item stored in the set is an NSMutableDictionary.
>
> And... you can't change the hash function used by NSMutableDictionary. So, for a custom class, you can implement a custom -hash method. But for NSMutableDictionary, you can't. And, if you do implement a custom -hash method for your custom class which is insensitive to the "contents" (whatever that might be) of its instances, then that's a _very_ different equality semantic than NSDictionary provides.
>
>
>> http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Founda
tion/Classes/NSDictionary_Class/Reference/Reference.html
>>
>> "Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key. However, the methods defined in this cluster insulate you from the complexities of working with hash tables, hashing functions, or the hashed value of keys. The methods described below take keys directly, not their hashed form."
>>
>> So, you may use your own hash function to create your keys... or not, as you wish.
>
> Um, the keys are not typically the output of a hash function. Well, you can use such for keys, but that's irrelevant to the hash of the dictionary object, itself.
>
>> But then the NSDictionary and NSMutableDictionary will hash the keys you pass to their methods using their own, internal, hash function, to determine where objects are stored in the dictionary.
>
> Right. So, if you mutate the dictionary while it's in the set, you will break things. That's why a set of mutable dictionaries (as opposed to immutable dictionaries, or some other data structure entirely) raises a huge red flag.
>
> As I previously cited, from the documentation of -[NSObject hash]:
> http://developer.apple.com/mac/library/documentation/cocoa/Reference/Founda
tion/Protocols/NSObject_Protocol/Reference/NSObject.html
>
> "If a mutable object is added to a collection that uses hash values to determine the objectâs position in the collection, the value returned by the hash method of the object must not change while the object is in the collection. Therefore, either the hash method must not rely on any of the objectâs internal state information or you must make sure the objectâs internal state information does not change while the object is in the collection. Thus, for example, a mutable dictionary can be put in a hash table but you must not change it while it is in there. (Note that it can be difficult to know whether or not a given object is in a collection.)"
>
> Note that this case of mutable dictionaries in a collection is explicitly warned about.
>
>
>> But my primary point was to ask the original poster to think about why he is using this arrangement of instances of NSMutableDictionary stored in an NSMutableSet.
>
> Yes, and that was my point, too.
Thanks for the insight guys - you have convinced me to re-think my data model. I never even began to conceive of some of the issues you have put on the table.
>
> -Ken
>


