Why do I have to define the class of the pointer? - objective-c

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.

Related

Objective C programming, passing message to NSDate

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];
}

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 need to retain the result of NSDateFormatter dateFromString:

I have an NSDate* that I'm storing as a property with the retain keyword:
#property (nonatomic, retain) NSDate* startTime;
I use it as follows:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"h:mm a"];
startTime = (NSDate*)[[NSUserDefaults] standardUserDefaults] objectForKey:#"StartTimeKey"];
if (startTime == nil)
startTime = [[dateFormatter dateFromString:#"8:00 am"] retain];
Why do I need to retain the result of the dateFromString: message, but I don't need to retain the result of objectForKey: ?
I just upgraded to XCode 4.2 and I'm now using the LLVM GCC 4.2 compiler. Before the upgrade, the code worked fine without the retain. Now it crashes (later in the code when I access the startDate property) without the retain message.
The problem is that you wrote this:
startTime = blah blah blah;
You're setting the instance variable startTime directly. If you do this instead:
self.startTime = blah blah blah;
then the compiler will turn it into this:
[self setStartTime:blah blah blah];
and the automatically-generated setter method will do the retain for you.
If you do this:
#synthesize startTime = _startTime;
then the instance variable will be named _startTime, making it easier to remember to use the property instead of assigning to the instance variable directly.
The answer is in the Memory Management Programming Guide on page 11.
You own an object you create (that is you do not need to retain it, it has been done ). You create an object with alloc, new, copy or mutablecopy.
In this case, the dateFormatter gives you a new object but, since you did not call alloc, new or copy yourself, dateFormatter will call (normally its how it works) autorealease on the new NSDAte object.
But, if you were setting your property using the setter and getter, you would no have this problem.

NSDateFormatter Incompatible Type Error

I am receiving the following error in Xcode:
warning: incompatible Objective-C types 'struct NSDate *', expected 'struct NSString *' when passing argument 1 of 'setUpdate:' from distinct Objective-C type
The error happens when I am trying to save the formatted string to myObj.update
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
//myObj is an object with instance variable 'update' as a NSString string
myObj.update = [formatter dateFromString:#"2011-03-17T18:15:05Z"];
[formatter release];
I know I am doing something wrong that is minor but can't pinpoint. Thanks for the help! :)
dateFromString: returns an instance of NSDate, not a string. You need to make your update property an NSDate. The compiler is basically telling you that the method is returning a date, but you're trying to assign that to a string property, and that's just not gonna be healthy!

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];
}