Understanding ObjC bad message examples? - objective-c

I'm currently reading The Big Nerd Ranch Guide on Objective C programming and I'm having trouble understanding a section in Chapter 13, Objects which is providing bad examples of Messages:
NSDate *now = [NSDate date];
double seconds = [now timeIntervalSince1970];
This is firstly a correct example of using messages in Objc. The first line creates a pointer to an NSDate object using the variable now and the NSDate type declaration. It is now an instance of NSDate. It was explained to me that if you are to print now, it will display the output of the NSDate date method. This is where things start to get a little weird for me. It seems odd that you would have a variable pointing both to an instance, and a function output if printed. But that's okay. Moving on.
The second line creates a variable called seconds, which is of type "double" and is going to obtain a value that the timeIntervalSince1970 method of NSDate instance now outputs.
Here is where the author introduces examples of bad messages:
double testSeconds = [NSDate timeIntervalSince1970];
NSDate *testNow = [now date];
He explains the errors as follows:
First Line:
"The Error is clear, the receiver in this message send is the NSDate
class, so the selector should be the name of an NSDate class method.
This selector is not."
Ok. This makes sense. An invalid method. Next. .
Second Line:
"This error is less clear: It is telling you that NSDate has no
instance method whose name matches the date selector."
Wait, what? Isn't now a pointer to an instance of NSDate? Shouldn't you be able to call the date method from an instance of NSDate? I don't get it.
The book does not explain any more than what I've quoted above, so I'm sure it's something stupid and basic I'm not getting. I hope this isn't too specific or unhelpful to others. I'll delete the submission if asked. Thank you.

The variable now points to an instance of NSDate. The method date is not defined on instances of NSDate but on the class itself. Hence date is a class method and not an instance method. Unlike other programming languages Objective-C does not inherit class methods to their class instances.
Vice versa instance methods cannot be called on classes. This said timeIntervalSince1970 cannot be called on the class NSDate as this method is an instance method. This is due to the circumstances that instances usually manage an instance state. Instance methods do operate on this instance state, namely reading and modifying their instance variables. Back to your example: The instance method timeIntervalSince1970 calculates the difference between 01/01/1970 and a concrete, instantiated date. So if you would be able to call timeIntervalSince1970 on class level there's no chance to calculate a difference of dates as the class NSDate doesn't carry any date information (the instance state!) such as the day, month, year and the time.
To sum up: Instance methods cannot be called on classes. This isn't supported by any programing language I am aware of. Calling class methods on instances is however supported by some programing language although there's typically no need for doing so and sometimes it even lowers code readability. However Objective-C doesn't support those calls neither.

Related

The lifetime of instance of object in ObjC

I'm still little bit perplexed by pointers and memory management (starting out with ObjC and Cocoa). What got me thinking, is this piece of code:
double seconds = [[NSDate date] timeIntervalSince1970];
This is what I understand:
I get value of float type returned from calling method/message on NSDate class
This value gets stored in variable seconds
What I don't understand is am I creating NSDate object (=instance of class NSDate) at all? Is this object only temporary?
I always thought that the way to create an object and have the object to be persistent (at least until ARC steps in or it is destroyed when function ends) is to create a pointer to it. Maybe like this:
NSDate *now = [NSDate date];
[now timeIntervalSince1970] // get the value
Does this mean that in my original example, there is some unnamed (no variable pointing to it) instance of NSDate created on the heap and once it returns the float value it gets removed from heap?
Does this mean that in my original example, there is some unnamed (no
variable pointing to it) instance of NSDate created on the heap and
once it returns the float value it gets removed from heap?
Yes, that's exactly right.
ARC will remove the object when it goes out of scope.
Even before ARC, the object would be created as autorelease and be released the next time through the event loop (or whenever the nearest autorelease pool was drained).

Strange apparent type inference by Objective-C compiler

Consider an Objective-C program with the following lines:
NSArray *records = dictionary[string];
NSDate *date = [records.lastObject date];
where records is an array of NSManagedObjects, which possess attributes date of type NSDate.
The compiler (in Xcode 6.1.1) gives the following warning for the 2nd line:
Incompatible pointer types initializing NSDate * with an expression
of type GTLDateTime.
How can this happen? I would not expect the compiler to try any inferences past records.lastObject being of type id. But it seems to try and arrive at an incorrect one.
So what is possibly going on here?
The problem is that somewhere is your project there is a method called date that returns a GTLDateTime object. Since .lastObject is of type id, xcode searches for a selector named "date" to try to infer the return type. If your code is correct, you should just cast [records.lastObject date] to NSDate or whatever the correct return type is, then you should be OK.

Why do [NSDate distantPast] and [NSDate distantFuture] methods return id?

I'm wondering why do [NSDate distantPast] and [NSDate distantFuture] methods' return types are of type id? Why don't these methods return an NSDate pointer?
Because polymorphism is just valid in one way.
Let's say that you subclass NSDate and that you want to override that method. You must use the same signature so you'll do it with this signature:
-(NSDate*) distantPast;
But you'll not be able to assign the result of the expression to a pointer of the subclass type, and you'll need to downcast the result:
NSDateSubclass* ptr= (NSDateSubclass*)[someDateSubclassInstance distantPast];
// Downcasting is necessary here, it would give a warning or syntax error otherwise.
Even if you are sure that the returned object is a subclass of NSDate, you need to downcast the result. This is why all the methods that returns created objects, are declared to return an id.
NSDate is an abstract superclass, and distantPast or distantFuture return private subclasses of NSDate, and not an NSDate per se.
I think that question is also like asking what should you return on an init method? An id or a pointer to our class.
Because it would be the same on that case you can consider it's some kind of standardization on Apple's part for class methods.
There is no particular reason for this. The other poster pointed out that these methods might return a private subclass of NSDate, but a subclass of NSDate is still an NSDate. I'm guessing that these methods are old enough (i.e. NextStep, before Mac OS) that the reasons for the id return type is historical, and "lost to antiquity". (If you look at these methods in the Mac OS docs, it says they were defined in Mac OS 10.0)
I bet the old school approach was to use anonymous object pointers everywhere.

Why does [NSDate date] return id?

Why doesn't it return NSDate*?
It's supposed to be a creation function right? All creation function return the class whose type is the class creating it.
Now, I can't do NSDate.date.timeIntervalSince1970 :(
I thought the compiler is smart enough to know that NSDate.date return NSDate even though the return type is id?
UPDATE
Starting from iOS 7, Apple is using instancetype as return type for most of the Foundation framework APIs (and other frameworks too), so now for instance the +date of NSDate has the following signature:
+ (instancetype)date
I just wrote a short article about it.
Original answer
Constructors and factory methods return id in order to allow subclasses to use them without ovverriding.
Imagine you have a subclass of NSDate called NSMutableDate.
If you call
[NSMutableDate date]
now you would expect to have a NSMutableDate * object back, but if date was returning NSDate * you would have needed to override that method changing the return type.
Using id allows this kind of flexibility.
Actually the clang compiler has the instancetype keyword that comes in handy in case you are defining your own factory methods.
I recently talked about this specific issue here.
The class factory methods (such as [NSMutableDate date], [NSMutableArray array], etc.) internally use alloc/init, and return whatever init returns.
init and its siblings (initWith...) always return an id, and by extension, so do the class factory methods.
It's a factory method providing a convenient way of creating new auto-released objects.
See this Apple guide.

What's the difference between NSDate and CFGregorianDate?

I have some objective-c code I'm converting from iPhone to iPad. CFGregorianDate is used throughout for date functions. I have never used it, preferring to use NSDate.
I want to take the date and format it so I get the full month name, using the format string "MMMM", which I don't seem to be able to do using CFGregorianDate.
What's the difference between the two, and does it make any difference in usage?
From the docs:
CFDate is “toll-free bridged” with its Cocoa Foundation counterpart, NSDate. What this means is that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object. In other words, in a method where you see an NSDate * parameter, you can pass in a CFDateRef, and in a function where you see a CFDateRef parameter, you can pass in an NSDate instance. This also applies to concrete subclasses of NSDate.
So, no difference in usage, really. The main difference is that CFDate and its subclasses are legacy types.
However, as has been pointed out, CFGregorianDate is extremely different from CFDate. It doesn't store a timestamp but rather the year, month, day, hour minute and second as separate integers.
So your best bet is to create a method like cfGregorianToNSDate: in which you parse these integers and construe a new NSDate object.