2

I'm trying to implement the protocol <NSCoding> for one of my classes. I've come across a problem while seralizing\deseralizing one of the property of this class. The property is of type: NSLocale.

Here a (Kiwi) test I wrote to understand the behaviour of NSLocale :

   NSLocale *locale = [NSLocale currentLocale];
   NSData *data = [NSKeyedArchiver archivedDataWithRootObject:locale];
   NSLocale *locale2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];

   NSLog(@"1) %@", [locale localeIdentifier]); //=> "en_US"
   NSLog(@"2) %@", [locale2 localeIdentifier]); //=> "en_US"
   [[locale should] equal:locale2]; //=>fail

   [[[NSLocale currentLocale] should] equal:[NSLocale currentLocale]]; //=> pass

The test fails. do you guys know why?

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
lucaconlaq
  • 1,511
  • 4
  • 19
  • 36

1 Answers1

3

NOTE

As per CodaFi comments below, this appears to be an issue with the implementation of NSLocale.

NSCoder produces an NSCFLocale instance, whereas straight allocation returns an NSLocale.

Due to the fact that NSLocale and NSCFLocale perform different equality checks (the former appears to be using CFEqual(), the latter isEqualToString:) this leads to the incongruences exposed below.


ORIGINAL POST

This is not exactly an answer, since I don't know what's going on, but I couldn't fit this in a comment. Here's the odd results of some testing

If you "manually" declare an instance of NSLocale everything works as expected

NSLocale * l1 = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:l1];
NSLocale *l2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%i", [l1 isEqual:l2]); // => 1, aka YES

But if you declare l1 as [NSLocale currentLocale], things go wrong

NSLocale * l1 = [NSLocale currentLocale];    
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:l1];
NSLocale *l2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%i", [l1 isEqual:l2]); // => 0, aka NO

Frankly I am as baffled as you are.


Extra info:

Here's the result of another test

NSLocale * l1 = [NSLocale currentLocale];
NSLocale * l3 = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:l1];
NSData *data1 = [NSKeyedArchiver archivedDataWithRootObject:l3];
NSLocale *l2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLocale *l4 = [NSKeyedUnarchiver unarchiveObjectWithData:data1];

NSLog(@"\n%p\n%p", l1, l2);    // => 0x9849da0
NSLog(@"\n%p\n%p", l3, l4);    // => 0x9866770
NSLog(@"%i", [l1 isEqual:l2]); // => 0x9866770
NSLog(@"%i", [l3 isEqual:l4]); // => 0x9866770

Surprisingly enough, the instance returned after unarchiving l1 is the same as l3!

Community
  • 1
  • 1
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • 3
    From what I can see from the ASM in CoreFoundation, some poor Apple engineer decided to do string equivalence with `CFEqual()` instead of `isEqualToString:`. Filing a radar. – CodaFi Jun 23 '13 at 18:58
  • on which string? localeIdentifier? – lucaconlaq Jun 23 '13 at 19:08
  • 3
    The problem is that NSLocale and NSCFLocale have two differing opinions on equivalence. NSCoder happens to be making you an NSCFLocale, where straight allocation is making you an NSLocale. – CodaFi Jun 23 '13 at 19:10
  • 1
    I see. You may post a link to the radar, so that other people will refer to it when reading this question – Gabriele Petronella Jun 23 '13 at 19:12