{"id":19718,"date":"2017-12-05T15:49:46","date_gmt":"2017-12-05T20:49:46","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=19718"},"modified":"2017-12-11T16:53:38","modified_gmt":"2017-12-11T21:53:38","slug":"key-difference-between-dictionary-and-nsdictionary","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2017\/12\/05\/key-difference-between-dictionary-and-nsdictionary\/","title":{"rendered":"Key Difference Between Dictionary and NSDictionary"},"content":{"rendered":"<p><a href=\"http:\/\/lapcatsoftware.com\/articles\/key-difference.html\">Jeff Johnson<\/a>:<\/p>\n<blockquote cite=\"http:\/\/lapcatsoftware.com\/articles\/key-difference.html\"><p>The recent article <a href=\"https:\/\/oleb.net\/blog\/2017\/11\/swift-4-strings\/\">Strings in Swift 4<\/a> by Ole Begemann talked about how Swift <code>String<\/code> equality is implemented as Unicode canonical equivalence. As a result, two String instances can be equal even if they contain different Unicode code units. [&#8230;] Two <code>NSString<\/code> instances are equal if they have the same sequence of UTF-16 code units.<\/p>\n<p>[&#8230;]<\/p>\n<p>This difference in behavior between Swift and Objective-C is troubling. Suppose that you had some old Objective-C code that saved user defaults in dictionary format with string keys, and then you wrote new Swift code to access the user defaults, naively using <code>dictionary(forKey:)<\/code>, because of course that&rsquo;s what you&rsquo;d think to use.<\/p><\/blockquote>\n<p>What happens is that you get a weird bridged Swift <code>Dictionary<\/code>. This is supposed to be an O(1) <a href=\"https:\/\/github.com\/apple\/swift\/blob\/master\/stdlib\/public\/core\/HashedCollections.swift.gyb\">wrapper<\/a> around the <code>NSDictionary<\/code>. Indeed, its <code>count<\/code> is the same. But when you look up the keys it uses the Swift rules for string quality. So a lookup that would find no matches in Objective-C may find one with Swift. Removing an entry removes multiple entries with equivalent keys. When you get the <code>description<\/code> it coalesces the entries with equivalent string keys, so that the <code>description<\/code> doesn&rsquo;t match the <code>count<\/code>. And if you try to cast it <code>as NSDictionary<\/code>, which should always succeed and just give you the underlying wrapped dictionary, it crashes.<\/p>\n\n<p>Update (2017-12-11): <a href=\"https:\/\/twitter.com\/jckarter\/status\/940265440953081856\">Joe<\/a> <a href=\"https:\/\/twitter.com\/jckarter\/status\/940266739895373824\">Groff<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/jckarter\/status\/940265440953081856\"><p>It&rsquo;s a known problem. Eventually we want to do away with the &ldquo;lazy bridging&rdquo;, which would allow the crash to at least happen deterministically on bridging and avoid the inconsistent view of the Dictionary.<\/p><\/blockquote><blockquote cite=\"https:\/\/twitter.com\/jckarter\/status\/940266739895373824\"><p>It&rsquo;s supposed to crash if it detects a key collision introduced by the difference in equality model (theory being that keys differing only by normalization form are almost always by mistake).<\/p><\/blockquote>\n<blockquote cite=\"https:\/\/twitter.com\/jckarter\/status\/940303060860993539\"><p>Another possibility is to opt out APIs from bridging when they deal with NSStrings that may significantly differ in normalization.<\/p>\n<p>We already know we need an opt-out mechanism for things like Core Data NSArray\/NSDictionary objects where their class-iness is part of the magic.<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/AirspeedSwift\/status\/940337644164194306\">Airspeed<\/a> <a href=\"https:\/\/twitter.com\/AirspeedSwift\/status\/940337859185205248\">Velocity<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/AirspeedSwift\/status\/940337644164194306\">\n<p>Bit of clarification: the crash is definitely a bug. The code to bridge into Swift from ObjC coalesces keys and shouldn&rsquo;t trap, but it assumes the count from the NSDictionary, an invalid optimization.<\/p>\n<\/blockquote>\n<blockquote cite=\"https:\/\/twitter.com\/AirspeedSwift\/status\/940337859185205248\"><p>O(1) bridging from Swift to ObjC only happens when the types in the ObjC container are verbatim-bridged. Which String\/NSString aren&rsquo;t.<\/p><\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Jeff Johnson: The recent article Strings in Swift 4 by Ole Begemann talked about how Swift String equality is implemented as Unicode canonical equivalence. As a result, two String instances can be equal even if they contain different Unicode code units. [&#8230;] Two NSString instances are equal if they have the same sequence of UTF-16 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"","apple_news_api_id":"","apple_news_api_modified_at":"","apple_news_api_revision":"","apple_news_api_share_url":"","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[131,69,54,71,901,258],"class_list":["post-19718","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-bug","tag-cocoa","tag-objective-c","tag-programming","tag-swift-programming-language","tag-unicode"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19718","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=19718"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19718\/revisions"}],"predecessor-version":[{"id":19812,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19718\/revisions\/19812"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=19718"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=19718"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=19718"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}