{"id":643,"date":"2010-07-08T12:55:29","date_gmt":"2010-07-08T17:55:29","guid":{"rendered":"http:\/\/vgable.com\/blog\/?p=643"},"modified":"2010-07-08T12:55:31","modified_gmt":"2010-07-08T17:55:31","slug":"nsdictionary-copies-its-keys","status":"publish","type":"post","link":"https:\/\/vgable.com\/blog\/2010\/07\/08\/nsdictionary-copies-its-keys\/","title":{"rendered":"NSDictionary Copies It&#8217;s Keys"},"content":{"rendered":"<p>An <code>NSDictionary<\/code> will <code>retain<\/code> it&#8217;s objects, and <code>copy<\/code> it&#8217;s keys. <\/p>\n<p>Here are some effects this has had on code I&#8217;ve worked on.<\/p>\n<ul>\n<li>\n<strong>Sometimes you get the same object you put in, sometimes not<\/strong>.<br \/>\n<a href=\"http:\/\/vgable.com\/blog\/2008\/11\/14\/prefer-copy-over-retain\/\">Immutable objects are optimized to return themselves as a <code>copy<\/code><\/a>. (But with some exceptions!). So the following code:<\/p>\n<pre>\n\tNSDictionary *d = [NSDictionary dictionaryWithObject:@\"object\" forKey:originalKey];\n\tfor(id aKey in d)\n\t\tif(aKey == originalKey)\n\t\t\tNSLog(@\"Found the original key!\");\n<\/pre>\n<p>Might print &#8220;Found the original key!&#8221;, and might not, depending on how <code>[originalKey  copy]<\/code> is implemented. For this reason, <strong>never use pointer-equality when comparing keys<\/strong>.<\/p>\n<li><strong>Mutable objects make bad keys<\/strong>. If <code>x<\/code> is a mutable <code>NSObject<\/code>, <code>[x copy] is an <\/code><em>immutable<\/em> copy of <code>x<\/code>, <em>at that point in time<\/em>. Any changes to <code>x<\/code> are <em>not<\/em> reflected in the copy. For example,\n<pre>\n\t[dict setObject:x forKey:key];\n\t\/\/...code that changes key, but not dict\n\t<a href=\"http:\/\/vgable.com\/blog\/2008\/12\/04\/nsassert-considered-harmful\/\">assert<\/a>([[dict objectForKey:key] isEqual:x]); \/\/fails!\n<\/pre>\n<p>Because the <code>copy<\/code> is an immutable object, it will blow up if you try to mutate it.<\/p>\n<pre>\n\tNSMutableString *key = \/\/something...\n\t[dict setObject:x forKey:key];\n\tfor(NSMutableString *aKey in dict)\n\t\t[aKey appendString:@\"2\"]; \/\/Error, aKey isn't mutable, even though key is!\n\t\t\n<\/pre>\n<\/li>\n<li>\n<strong>View objects make bad keys<\/strong>. Views have state related to  the screen: their <code>frame<\/code>, position in the view hierarchy, animation layers, etc. When you <code>copy<\/code> a view object, the copy won&#8217;t (always) be <code>isEqual:<\/code> to the original, because it&#8217;s not on the screen in exactly the same way.\n<\/li>\n<\/li>\n<li>\n<strong>Your classes must support <code>NSCopying<\/code> to be used as a key in an <code>NSDictionary<\/code><\/strong>, you can&#8217;t just <a href=\"http:\/\/mikeash.com\/pyblog\/friday-qa-2010-06-18-implementing-equality-and-hashing.html\">implement <code>-hash<\/code> and <code>-isEqual:<\/code><\/a> in your custom classes.\n<\/li>\n<\/ul>\n<p>Of course, this isn&#8217;t a complete list of every way key-copying can trip you up. But if you understand what <code>copy<\/code> means in Cocoa, and remember how <code>NSDictionary<\/code> works, you&#8217;ll be able to avoid or quickly solve any issues.<\/p>\n<h3>How to Document Such Behavior Better Than Apple Did<\/h3>\n<p>Given what we know about <code>NSDictionary<\/code>, what&#8217;s wrong with the following snippit from <code>NSDictionary.h<\/code>?<\/p>\n<pre>\n@interface NSMutableDictionary : NSDictionary\n- (void)setObject:(id)anObject forKey:(id)aKey;\n@end\n<\/pre>\n<p>Answer: <code> aKey <\/code> needs to implement <code>NSCopying<\/code>, so it should be of type <code>(id&lt;NSCopying&gt;)<\/code> instead of type <code>(id)<\/code>. That way, the header is self-documenting, and, if like most smart programmers, you&#8217;re using autocomplete to type out Cocoa&#8217;s long method names, the auto-completed template will be self-documenting too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An NSDictionary will retain it&#8217;s objects, and copy it&#8217;s keys. Here are some effects this has had on code I&#8217;ve worked on. Sometimes you get the same object you put in, sometimes not. Immutable objects are optimized to return themselves as a copy. (But with some exceptions!). So the following code: NSDictionary *d = [NSDictionary [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,6,203,3,5,4],"tags":[167,601,144,108],"class_list":["post-643","post","type-post","status-publish","format-standard","hentry","category-bug-bite","category-cocoa","category-iphone","category-macosx","category-objective-c","category-programming","tag-apple","tag-copy","tag-documentation","tag-nsdictionary"],"_links":{"self":[{"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/posts\/643","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/comments?post=643"}],"version-history":[{"count":1,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/posts\/643\/revisions"}],"predecessor-version":[{"id":644,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/posts\/643\/revisions\/644"}],"wp:attachment":[{"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/media?parent=643"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/categories?post=643"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vgable.com\/blog\/wp-json\/wp\/v2\/tags?post=643"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}