10

NSDate conforms to NSCopying protocol. According to the documentation for NSCopying protocol:

a copy must be a functionally independent object with values identical
to the original at the time the copy was made.

But, when I do this:

NSDate *date1 = [NSDate date];
NSDate *date2 = [date1 copy];
NSLog(@"result: date1 0x%x  date2 0x%x", (int)date1, (int)date2);
// "result: date1 0x2facb0  date2 0x2facb0"

The two objects are identical (same object id). What am I missing? How do I get an independent object as a copy?

Jeff
  • 2,659
  • 1
  • 22
  • 41
  • 3
    You don't need an independent object. `NSDate`s are immutable. You can't change them so it doesn't matter if `-copy` returns the same object. – JeremyP Oct 25 '11 at 07:52
  • It should be noted that the Objective-C architecture permits certain immutable "objects" to be implemented as self-contained values in pointers. Ie, with several reserved bits in the pointer to identify type, the remainder of the pointer value can actually BE the "object". I don't know that Apple has admitted doing this with any objects, but it's clearly feasible with NSDate objects and several of the NSNumber flavors. – Hot Licks Dec 04 '14 at 19:52

3 Answers3

29

copy does not guarantee different object pointer. “Functionally independent” means that changes to the original object will not be reflected in the copy, and thus for immutable objects copy may work as retain (I don't know if this is guaranteed though, probably not).

Try date2 = [[NSDate alloc] initWithTimeInterval:0 sinceDate:date1].

hamstergene
  • 24,039
  • 5
  • 57
  • 72
3

Beware!

I recently found out, that on iOS 8.1(.0) [NSDate dateWithTimeInterval:0 sinceDate:date1] returns date1! Even the alloc/init returns the same object.

The deep-copy was important for me, as I create copies of objects. Later I compare the timestamps with [date1 laterDate:date2] == date2 which will always be true, if the deep-copy doesn't work.

Same for [date1 dateByAddingTimeInterval:0]

I have no good solution for iOS 8.1, yet, but keep searching and will update here. An emergency-workaround could be to create a date-string with a formatter, and then create a date from the string with the same formatter.

Edit: It get's even worse:

NSString *date1String = [iso8601DateFormatter stringFromDate:date1];
date2 = [iso8601DateFormatter dateFromString:date1String];

(lldb) p date1
(NSDate *) $0 = 0xe41ba06fd0000000 2014-11-03 01:00:00 CET
(lldb) p date2
(NSDate *) $1 = 0xe41ba06fd0000000 2014-11-03 01:00:00 CET
Felix Lieb
  • 414
  • 3
  • 14
  • Erm, Sir, did you read it? It's all about deep-copying an NSDate. My answer extends the solution by the warning, that it won't work under iOS 8.1. – Felix Lieb Dec 04 '14 at 17:02
  • What problem will copying the NSDate solve? (What purpose does comparing the dates with `==` serve?) – Hot Licks Dec 04 '14 at 17:12
  • Erm, Sir, did you read it, again? I quote my answer: _...I compare the timestamps with [date1 laterDate:date2] == date2 which will always be true, if the deep-copy doesn't work._ Simple example: You create some object with a timestamp-property. At some point, you want to create a deep-copy of the object for any reason, like moving to another context, where it needs to be a different object. At some point you compare the objects of both contexts to find the newer ones and [date1 laterdate:date2] == date2 would always be true. – Felix Lieb Dec 04 '14 at 17:24
  • It actually doesn't matter, as my answer still is an extension to warn readers about the chosen solution, that this kind of deep-copying won't work under iOS 8.1. The purpose for deep-copying is completely irrelevant. – Felix Lieb Dec 04 '14 at 17:25
  • 2
    Since there is no need to copy NSDate objects, your "warning" is irrelevant. – Hot Licks Dec 04 '14 at 17:27
  • (Odd that you don't get your shorts equally in a twist over NSString.) – Hot Licks Dec 04 '14 at 17:28
  • Well I get the point why copying (assuming you talk about deep-copying) of NSDates might be irrelevant. It would have been helpful in the first place, if you had written an answer to the original question, at best, explaining why it is irrelevant. Nevertheless, **if** you want to deep-copy an NSDate, for whatever reason, maybe because it's fun to compare pointers, the warning is absolutely relevant. There is an accepted answer telling the questioner how to achieve a deep-copy, which doesn't work under iOS 8.1. My "warning" clarifies - completely apart from relevancy of deep-copying NSDates. – Felix Lieb Dec 05 '14 at 09:39
  • So, how do you "deep copy" an NSNull? – Hot Licks Dec 05 '14 at 12:38
  • This question is about deep copying of NSDate-objects. If you really want to know that, I suggest you search it, or create a new question here on SO. If you create one, I might even answer there or mark it as a duplicate with an appropriate link. As SO states right above the textfield **"Please avoid extended discussions in comments."** and I feel no need to chat with a troll, I wish you a nice day - completely without deep-copying of NSDate-objects ;o) – Felix Lieb Dec 05 '14 at 13:35
  • In my case, I have the problem with associative objects in this context. So I've added a new question about it to SO: http://stackoverflow.com/questions/30308082/how-to-make-nsdate-with-associative-object-work – John May 18 '15 at 16:13
0

NSDate is abstract class. It often uses 'tagged pointer' trick so it will impossible to get different objects for same dates in most cases. Real class name NSTaggedDate in this case. Some dates could be real object (if there is no space in tagged pointer for it). Ex:

    [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:1234567890123456]
Maxim Kholyavkin
  • 4,463
  • 2
  • 37
  • 82