{"id":1826,"date":"2012-03-13T18:25:09","date_gmt":"2012-03-13T17:25:09","guid":{"rendered":"http:\/\/t-machine.org\/?p=1826"},"modified":"2012-03-13T18:25:09","modified_gmt":"2012-03-13T17:25:09","slug":"2-reasons-to-beware-apples-nsdictionary-and-coredata","status":"publish","type":"post","link":"http:\/\/new.t-machine.org\/index.php\/2012\/03\/13\/2-reasons-to-beware-apples-nsdictionary-and-coredata\/","title":{"rendered":"2 reasons to beware Apple&#8217;s NSDictionary, and CoreData"},"content":{"rendered":"<p>Ever seen this hard to notice bug in an iPhone \/ Mac project? Caused by a flaw (bug?) in Apple&#8217;s own API:<\/p>\n<blockquote><p>\nNSDictionary * map;<br \/>\n&#8230;<br \/>\nNSObject * key;<br \/>\nNSObject * value;<br \/>\n&#8230;<br \/>\n\/\/ NB: next line will frequently crash at runtime<br \/>\n\/\/ &#8230;but even if doesn&#8217;t crash, it probably won&#8217;t do<br \/>\n\/\/     what you&#8217;d expect it to:<br \/>\n[map setObject:value forKey:key];<\/p>\n<p>if( value != [map objectForKey:key] ) \/\/ HA!<br \/>\n{<br \/>\n    NSAssert( 1 == 0, @&#8221;What the **** just happened??&#8221; );<br \/>\n}\n<\/p><\/blockquote>\n<p>NSDictionary is one of the nastier things I&#8217;ve seen in a programming language: in an Object-Oriented Language, it&#8217;s a class that refuses to take Objects as arguments, *but pretends to*. If you attempt it, it either crashes, or it invalidates your objects, breaking contracts all over the place.<\/p>\n<h4>ObjC&#8217;s bizarre design<\/h4>\n<p>In the days pre-OOP, a &#8220;dictionary&#8221; was something that mapped:<\/p>\n<blockquote><p>\n &#8220;a string&#8221;<br \/>\nto<br \/>\n&#8220;anything&#8221; (usually: basic datatypes &#8211; e.g. strings, integers, floats, etc).\n<\/p><\/blockquote>\n<p>In the days of OOP, the same thing is usually called a &#8220;map&#8221; (which is a better term) &#8211; although the terms are synonymous &#8211; and maps:<\/p>\n<blockquote><p>\n&#8220;an object&#8221;<br \/>\nto<br \/>\n&#8220;another object&#8221;\n<\/p><\/blockquote>\n<p>What did Apple\/NextStep\/Crazy-Guys-Behind ObjC do?<\/p>\n<p>NSDictionary: maps:<\/p>\n<blockquote><p>\n&#8220;STRINGS ONLY&#8221; (no objects allowed!)<br \/>\nto<br \/>\n&#8220;OBJECTS ONLY&#8221; (no core datatypes allowed!)\n<\/p><\/blockquote>\n<h4>But &#8230; I can use an NSObject as key?<\/h4>\n<p>Yep &#8211; but Apple&#8217;s internal implementation takes a *copy* of the object, and uses that as a key &#8211; rather than using the object that you gave it. This is a common problem in OOP languages and implementations of Map &#8211; e.g. Java does the same thing.<\/p>\n<p>Unfortunately, this means that you can call &#8220;setObject:forKey:&#8221;, and then &#8220;objectForKey:&#8221; will return nil *for the same key*.<\/p>\n<p>In Java and other OOP languages, you are required to implement a custom &#8220;isObjectEqualToObject&#8221; method. In ObjC too &#8211; except that that method is ILLEGAL if you&#8217;re using CoreData.<\/p>\n<h4>And ObjC will crash too, as a bonus<\/h4>\n<p>I&#8217;ve never seen this in other OOP languages, but in ObjC *additionally*: if you don&#8217;t manually add <NSCopying> to the object you pass-in &#8230; it crashes at runtime. Yay!<\/p>\n<p>How come? AFAICT, Apple&#8217;s header file is wrong:<\/p>\n<blockquote><p>\n&#8211; (void)setObject:(id)anObject forKey:(id)aKey \/\/ You lie!\n<\/p><\/blockquote>\n<p>it seems the implementation of that method is:<\/p>\n<blockquote><p>\n&#8211; (void)setObject:(id)anObject forKey:(id&lt;NSCopying&gt;)aKey; \/\/ the correct signature?\n<\/p><\/blockquote>\n<p>Net result? Code that happily compiles &#8230; will crash. ARGH!<\/p>\n<h3>Two reasons to beware&#8230; <\/h3>\n<p>&#8230;so what&#8217;s the other one?<\/p>\n<p>Ah, just the one we see again and again on live projects, wasting hours and hours of time:<\/p>\n<blockquote><p>\nNSDictionary* dictionary = [NSDictionary dictionaryWithObjectsAndKeys:<br \/>\n     object1, key1,<br \/>\n     object2, key2,<br \/>\n     nil];<\/p>\n<p>NSLog( @&#8221;dictionary = %@&#8221;, dictionary ); \/\/ but it only has one key\/value pair. ?!?!?\n<\/p><\/blockquote>\n<p>Ah, well &#8230; that nil &#8230; what happens when object2 is nil? Oh, damn.<\/p>\n<p>What about if key2 is nil? Now we&#8217;re really nasty &#8230; we&#8217;ve given it &#8220;half&#8221; of key\/value pair. Nice!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ever seen this hard to notice bug in an iPhone \/ Mac project? Caused by a flaw (bug?) in Apple&#8217;s own API: NSDictionary * map; &#8230; NSObject * key; NSObject * value; &#8230; \/\/ NB: next line will frequently crash at runtime \/\/ &#8230;but even if doesn&#8217;t crash, it probably won&#8217;t do \/\/ what you&#8217;d [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,20],"tags":[],"_links":{"self":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/1826"}],"collection":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/comments?post=1826"}],"version-history":[{"count":0,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/1826\/revisions"}],"wp:attachment":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/media?parent=1826"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/categories?post=1826"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/tags?post=1826"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}