Objective C programming, passing message to NSDate - objective-c

I am following a book for objective-C and came across this example to print the current date using NSLog statement. I am confused why the NSDate class was not instantiated(alloc and init) before passing the date message to it.
NSDate * pointerToIt = [NSDate date];
Further down the code another message was passed to this pointer..
[pointerToIt timeIntervalSince1970];
What I knew is as long as the pointer holds the address to instance of a class messages can be sent to it but the class was never instantiated and still the messages are being passed. Can someone throw some light on this for me ?

The date is a somewhat special way to get the current date. It is a static method on the NSDate class which does the following:
Creates and returns a new date set to the current date and time.
This method uses the default initializer method for the class, init.
Your code is pretty much the same as in the docs:
NSDate *today = [NSDate date];
Therefore the today object is in fact implicitly correctly initalized.

[NSDate date] did the alloc and init for you. If you refer to the documentation you will read.
Creates and returns a new date set to the current date and time.

[NSDate date] is a class factory method. Internally, it would look something like this:
+ (instancetype)date {
return [[self alloc] init];
}

Related

initialize an instance in objective-C with alloc and init

I am learning Objective-C and I've just read about alloc and int methods. Before this point,when I wanted to create an instance of NSDate for example, I coded:
NSDate *now = [NSDate date];
Now I saw that the above can be written like this
NSDate *now = [[NSDate alloc] init];
Are the above do the same thing? As i have understood (hopefully correct) the first one creates and instance of NSDate, by sending the message date to the class NSDate. The second one, it just allocates space for the instance and initialize it, so that it is ready to work.
You would think the two things you list were definitely not the same thing based on a background knowledge of Objective-C, if you weren't familiar with NSDate. But in fact, they are the same thing in this case.
[NSDate date] is calling an NSDate class method that returns an NSDate object set to the current date and time.
Normally, a method call like [[NSDate alloc] init] would instantiate a new default object of the type requested, so you might expect that this would not be set to any date/time. However, the default NSDate object is in fact initialised with the current date and time, as discussed in the documentation, so in this particular case—they are the same thing.
As an aside, as with most NSObjects, you can also just call [NSDate new] to get the same effect as [[NSDate alloc] init] (and thus the same effect in this case as [NSDate date]).

Why do I have to define the class of the pointer?

I'm studying Objc with the book from BigNerdRanch 'Objective-C Programming from Aaron Hillegass' and there's this thing that keeps puzzling me.
I understand that the complier needs to know what kind of variable I'm talking about so i have to declare the var type before assigning a value.
int myNum = 10;
Fine. But when it comes to ObjC classes, what's the reason for declaring the type of pointer if I have to declare it right after the =, when I alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
Obviously the *dateFormatter object is an istance of NSDateFormatter, I wrote it in the allocation. Why do i have to declare it at the beginning too?
'Cause if I try to do something like
NSString *timeZone = [NSTimeZone systemTimeZone];
Xcode clearly warns me with 'Incompatible pointer types initializing 'NSString *' with an expression of type 'NSTimeZone *'.
I feel like I'm missing something. Sorry if it's a dumb question, trying to learn.
Here the real question is rather "Why do I have to define the correct class of the pointer?"...
The answer is: you may want to use the variable in some other context as well. If you message [NSTimeZone systemTimeZone] directly, then the compiler may be able to deduce the type, but what if you message the variable? If you go with the weaker-typed
id tz = [NSTimeZone systemTimeZone];
then there's much less opportunity for the compiler to check for errors if you use tz where an NSTimeZone * is expected than it could if you declared it as NSTimeZone *tz.
As an even clearer example, suppose you have a method:
- (NSTimeZone *) userSpecifiedTimeZone {
id timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName;
return timeZone;
}
See the bug?
Xcode won't catch it, since it's perfectly valid to assign any object to a variable of type id, and to return an id from a method whose return type is any object type, and to subsequently assign that id to another id variable, or try to send messages to it, in the calling code.
You'll find this bug—if you don't catch it early with your own human eyes—only at run time, and only when the user enters a bogus time zone name, and you then try to use that time zone name (wrongly returned as this method's result) as an NSTimeZone object.
Compare to the statically-typed version:
- (NSTimeZone *) userSpecifiedTimeZone {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName; //Clang calls shenanigans here
return timeZone;
}
Clang rightly objects that assigning an NSString * to a variable typed as NSTimeZone * is suspicious.
You don't have to define the class of the pointer, but the potential otherwise for bugs like the one shown above is why we do it.
But when it comes to ObjC classes, what's the reason for declaring the
type of pointer if I have to declare it right after the =, when I
alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
You're not declaring the type of pointer twice. There's a lot going on in this statement. The first occurrence of 'NSDateFormatter' is telling the compiler that dataformatter is a pointer to this type of object, whereas the second occurrence of 'NSDateFormatter' is calling the 'alloc' method in the NSDateFormatter class. Same word, two completely different meanings.
The first thing that happens is [NSDateFormatter alloc] which is calling the (class) method 'alloc' in the 'NSDateFormatter' class. This returns an (empty) instance of an NSDateFormatter object in which the method 'init' is called. A pointer to the resultant object is then stored in your 'dateFormatter' variable, and we tell the compiler that this is a pointer to an NSDateFormatter object.
Think of it like this:
NSDateFormatter *dateFormatter; Create a pointer to an NSDateFormatter object.
newDate = [NSDateFormatter alloc]; Create an empty NSDateFormatter object by calling the class method alloc in NSDateFormatter
[newDate init]; Initialise it by calling the onject's 'init' method
dateformatter = *newDate; Assign a pointer to it to my variable.

Setting today's date to NSDate

I have a NSDate property as below and I wish to set it with today's date.
#property (nonatomic, copy, readwrite) NSDate *todayDate;
I tried the following but I am getting an error:
NSDate *date = [[NSDate alloc] init];
self.todayDate = [date isToday];
I guess, isToday just checks whether the date is today's date and doesn't set the date.
I am getting following 2 errors:
Implicit conversion of 'BOOL' (aka 'signed char') to 'NSDate *' is disallowed with ARC
Incompatible integer to pointer conversion assigning to 'NSDate *' from 'BOOL' (aka 'signed char');
How do I set NSDate to today's date in Objective C?
NSDate has you covered:
NSDate *today = [NSDate date];
This is a common pattern in Apple's frameworks: semantic "factory" class methods. This call replaces your calls to alloc and init, and is a preferred way to work with common objects. It's also a nice pattern to emulate in your own classes :-)
On of the core concepts which should shed light on the "why" is that of immutability. Many objects (NSString, NSNumber, etc) are considered (at least in practice) to be immutable. This means they get their value exactly once: during init. This leads the the "other" answer also being an instantiation-time technique: a custom initializer.
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow: 0];
This will set your property to todays date. The error is because, as you suspect, you are assigning a boolean to a pointer.
self.todayDate = [NSDate date];

Objective-C : Changing "self" value inside self

I have a category on NSDate, and I want to implement some functions to manipulate the date, like :
NSDate *thedate = [NSDate date];
[thedate setToMidnight];
so I have a function in NSDate like :
-(void)setToMidnight {
some code with calendars and comps
self = theNewDate;
}
This works inside the function, but outside this member function, thedate has not changed.
I understand this malfunction because I've been told that self is just a local variable created inside the member function.
So, how can I make this work ?
Of course, I could have written :
thedate = [thedate dateAsMidnightDate]
or thedate = [NSDate dateAtMidnightFromDate:thedate]
but I feel it has more sense inside the instance class, as I don't want to change the date but just adjust some values of the previously created one.
You can't. Specifically:
A function or method cannot modify local variables in the calling context.
NSDate is not mutable (i.e. you can't modify an NSDate once it's created).
Therefore, no such method can be written. The closest you can get would be a class that wraps an NSDate and forwards messages to that internal NSDate object, which reassigns the date instance variable when you want to make it represent a new date.

NSDate init question, related to memory management in Objective-C

I have an NSDate object created by
NSDate *date = [[NSDate alloc] init];
Later, I want to reset the date to the "now", so I thought that
[date init];
or
date = [date init];
might do the job, but they don't. Instead,
[date release];
date = [[NSDate alloc] init];
works. I'm a bit confused about this, since in the documentation for - (id) init, it says:
Returns an NSDate object initialized to the current date and time.
and since date is already allocated, shouldn't it just need an init message?
Think of alloc and init as logically inseparable halves of a constructor. You can only call methods beginning with "init" once on a given object — once the object has been initialized, and it's an error to initialize it again. This is true for any Objective-C object, not just NSDate. However, NSDate objects are also immutable — once created, they can't change.
The reason the latter code works is because you're creating a new instance of NSDate, which is the correct thing to do. You can also use [NSDate date] to accomplish the same thing. Be aware that it returns an object that you don't (yet) own, so you'll need to retain it if you need to keep it around, and release it later.
Be aware that if you receive an object from someone, it has already been initialized. (If not, it's a programming error in the code that provided it, or is an extremely uncommon exception to the rule.)
If you want to get the current date you can just use:
NSDate * now = [NSDate date];
If you want to keep it then retain it.
NSDate * now = [[NSDate date] retain];
You can't reset NSDate with init, init is only for initializing the object for the first time.
You could just get another date:
NSDate * now = [[NSDate date] retain];
// use the now object
// need new date
[release now];
now = [[NSDate date] retain];
// once you don't need it release it
[now release];
The date message returns autoreleased instance of NSDate, hence the release or autorelease.
The autorelease is used for cases where you don't want to worry about where exactly you need to release the object - it is put into autorelease pool. Object in autorelease pool are released after the end of event loop iteration, or when you call release on pool ... (see more in Apple docs about memory management).
Btw. the [NSDate date] is a convenience method it's probably something like (not quaranteed to be exactly the same but functionally similar to):
- (NSDate *)date
{
return [[[NSDate alloc] init] autorelease];
}