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.
Related
In the header for NSLocale, currentLocale is declared like this:
+ (id /* NSLocale * */)currentLocale; // an object representing the user's current locale
It's obvious that they are returning id on purpose, but I'm curious why that would be necessary. Could this method ever return anything other than an NSLocale instance?
Back in the day, one used NSDictionary objects for locale information. See, for example, the "Special Considerations" documented for -[NSString compare:options:range:locale:]:
Special Considerations
Prior to OS X v10.5, the locale argument was an instance of NSDictionary. On OS X v10.5 and later, if you pass an instance of NSDictionary the current locale is used instead.
Some methods, such as -[NSDate dateWithNaturalLanguageString:locale:] still take an NSDictionary.
Other methods, such as many classes' -descriptionWithLocale:, can take either.
Anyway, with the introduction of NSLocale the types of various locale parameters was generalized to id to accommodate either kind of object without breaking source compatibility. The return type of +[NSLocale currentLocale] is similar generic so that it can be passed to methods that used to only take NSDictionary objects.
Initializers (even convenience initializer) traditionally return id. This prevents problems when you subclass. For instance, imagine this scenario:
#interface Foo : NSObject
- (Foo *)initWithBar:(Bar *)bar;
#end
#interface Baz : Foo
- (Baz *)initWithBar:(Bar *)bar;
#end
This would be a compiler error. You are redefining initWithBar: to return a different type. But if you always return Foo*, then Baz *baz = [Baz initWithBar:bar] would fail because initWithBar: returns a superclass.
To get yourself out of this problem, all initializers have historically returned id if there's any chance the class will be subclassed (which is to say, you should really always do this).
Recently, clang added instancetype, which solves this problem more elegantly by representing "the type of the current class." This is only useable in the interface. You can't declare variables to be of type instancetype (I've actually wanted this in some cases…) id is automatically promoted to instancetype for methods that begin with init…. Otherwise, you need to use it manually. Many older Cocoa interfaces haven't been updated yet, but they're slowly moving over to instancetype.
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.
Clang adds a keyword instancetype that, as far as I can see, replaces id as a return type in -alloc and init.
Is there a benefit to using instancetype instead of id?
Yes, there are benefits to using instancetype in all cases where it applies. I'll explain in more detail, but let me start with this bold statement: Use instancetype whenever it's appropriate, which is whenever a class returns an instance of that same class.
In fact, here's what Apple now says on the subject:
In your code, replace occurrences of id as a return value with instancetype where appropriate. This is typically the case for init methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of id to return instancetype, it doesn’t convert other methods. Objective-C convention is to write instancetype explicitly for all methods.
Emphasis mine. Source: Adopting Modern Objective-C
With that out of the way, let's move on and explain why it's a good idea.
First, some definitions:
#interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
#end
For a class factory, you should always use instancetype. The compiler does not automatically convert id to instancetype. That id is a generic object. But if you make it an instancetype the compiler knows what type of object the method returns.
This is not an academic problem. For instance, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] will generate an error on Mac OS X (only) Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes. The reason is that both NSFileHandle and NSURLHandle provide a writeData:. Since [NSFileHandle fileHandleWithStandardOutput] returns an id, the compiler is not certain what class writeData: is being called on.
You need to work around this, using either:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
or:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Of course, the better solution is to declare fileHandleWithStandardOutput as returning an instancetype. Then the cast or assignment isn't necessary.
(Note that on iOS, this example won't produce an error as only NSFileHandle provides a writeData: there. Other examples exist, such as length, which returns a CGFloat from UILayoutSupport but a NSUInteger from NSString.)
Note: Since I wrote this, the macOS headers have been modified to return a NSFileHandle instead of an id.
For initializers, it's more complicated. When you type this:
- (id)initWithBar:(NSInteger)bar
…the compiler will pretend you typed this instead:
- (instancetype)initWithBar:(NSInteger)bar
This was necessary for ARC. This is described in Clang Language Extensions Related result types. This is why people will tell you it isn't necessary to use instancetype, though I contend you should. The rest of this answer deals with this.
There's three advantages:
Explicit. Your code is doing what it says, rather than something else.
Pattern. You're building good habits for times it does matter, which do exist.
Consistency. You've established some consistency to your code, which makes it more readable.
Explicit
It's true that there's no technical benefit to returning instancetype from an init. But this is because the compiler automatically converts the id to instancetype. You are relying on this quirk; while you're writing that the init returns an id, the compiler is interpreting it as if it returns an instancetype.
These are equivalent to the compiler:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
These are not equivalent to your eyes. At best, you will learn to ignore the difference and skim over it. This is not something you should learn to ignore.
Pattern
While there's no difference with init and other methods, there is a difference as soon as you define a class factory.
These two are not equivalent:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
You want the second form. If you are used to typing instancetype as the return type of a constructor, you'll get it right every time.
Consistency
Finally, imagine if you put it all together: you want an init function and also a class factory.
If you use id for init, you end up with code like this:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
But if you use instancetype, you get this:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
It's more consistent and more readable. They return the same thing, and now that's obvious.
Conclusion
Unless you're intentionally writing code for old compilers, you should use instancetype when appropriate.
You should hesitate before writing a message that returns id. Ask yourself: Is this returning an instance of this class? If so, it's an instancetype.
There are certainly cases where you need to return id, but you'll probably use instancetype much more frequently.
There definitely is a benefit. When you use 'id', you get essentially no type checking at all. With instancetype, the compiler and IDE know what type of thing is being returned, and can check your code better and autocomplete better.
Only use it where it makes sense of course (i.e. a method that is returning an instance of that class); id is still useful.
Above answers are more than enough to explain this question. I would just like to add an example for the readers to understand it in terms of coding.
ClassA
#interface ClassA : NSObject
- (id)methodA;
- (instancetype)methodB;
#end
Class B
#interface ClassB : NSObject
- (id)methodX;
#end
TestViewController.m
#import "ClassA.h"
#import "ClassB.h"
- (void)viewDidLoad {
[[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime
[[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible #interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}
You also can get detail at The Designated Initializer
**
INSTANCETYPE
**
This keyword can only be used for return type, that it matches with return type of receiver. init method always declared to return instancetype.
Why not make the return type Party for party instance, for example?
That would cause a problem if the Party class was ever subclassed. The subclass would inherit all of the methods from Party, including initializer and its return type. If an instance of the subclass was sent this initializer message, that would be return? Not a pointer to a Party instance, but a pointer to an instance of subclass. You might think that is No problem, I will override the initializer in the subclass to change the return type. But in Objective-C, you cannot have two methods with the same selector and different return types (or arguments). By specifying that an initialization method return "an instance of the receiving object," you would never have to worry what happens in this situation.
**
ID
**
Before the instancetype has been introduced in Objective-C, initializers return id (eye-dee). This type is defined as "a pointer to any object". (id is a lot like void * in C.) As of this writing, XCode class templates still use id as the return type of initializers added in boilerplate code.
Unlike instancetype, id can be used as more than just a return type. You can declare variables or method parameters of type id when you are unsure what type of object the variable will end up pointing to.
You can use id when using fast enumeration to iterate over an array of multiple or unknow types of objects. Note that because id is undefined as "a pointer to any object," you do not include an * when declaring a variable or object parameter of this type.
The special type instancetype indicates that the return type from the init method will be the same class as the type of object it is initializing (that is, the receiver of the init message). This is an aid for the compiler so that it can check your program and flag potential
type mismatches—it determines the class of the returned object based on context; that is, if you’re sending the init message to a newly alloc’ed Fraction object, the compiler will infer that the value returned from that init method (whose return type has been declared as type instancetype) will be a Fraction object. In the past the return type from an initialization method was declared as type id. This new type makes more sense when you consider subclassing, as the inherited initialization methods cannot explicitly define the type of object they will return.
Initializing Objects, Stephen G. Kochan, Programming in Objective-C, 6th Edition
Why do some objects not need to be initialized before use in objective-c?
For example why is this NSDate *today = [NSDate date]; legal?
They are initialized within the date method. This is a common way to create autoreleased objects in Objective-C. Allocators of that form are called convenience allocators.
To learn more about that, read the "Factory Methods" paragraph in Apple's Cocoa Core Competencies document about Object Creation: http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ObjectCreation.html
To create convenience allocator for you own classes, implement a class method, named after your class (without prefix). e.g.:
#implementation MYThing
...
+ (id)thing
{
return [[[MYThing alloc] init] autorelease];
}
...
#end
today is initialized (and autoreleased) inside the static date call.
You only need to called an init… method on objects you have allocated by calling alloc. alloc only reserves space needed for the object, creating a an unitialized object.
An uninitialized object have all instance variables set to zero, nil, or equivalent for the type. Except for the retain count that is set to 1.
All other methods that return an object are guaranteed to return a fully initialized object. alloc is the exception.
You must never call an init… method on an object that is already initialized. Simple rule on thumb is to use a 1-to-1 relation between alloc-init…, thats it.
Two parts.
First, as others have mentioned, a method can initialise and then autorelease an object before returning it. That's part of what's happening here.
The other part is how it's defined. Note how most Objective C definitions begin with a -? The one you mention does not. The signature looks like this:
+ (NSDate*) date;
That is, it's a class method and applies to the class as a whole rather than to an instance of that class.
In Java, it is very easy to code the following design:
public abstract class Pizza {
public static final Pizza.NULL = new Pizza() {
/* "null" implementations */
}
/* actual/abstract implmentations */
}
What is the preferred method to attain the same efficient scenario in Objective-C? I have been unable to find any documentation on the subject, and I have tried a couple different scenarios with static const, #define etc. but none of them seem to work out as well as the Java method above.
I would like to avoid writing a concrete NullPizza class that has a static method to obtain the singleton instance, as it seems more 'proper' for it to be some final property/field of the highest-level interface. (Pizza, in this case.)
Edit: While I understand how the NULL pattern specifically would be handled due to Obj-C's unique method of handling method calls to 'nil', what about other static common instances, such as Response.YES and Response.NO? (See comments for discussion.)
There is no need for this type of pattern in Objective-C because it is not considered a runtime error to message a nil instance of a class. If the method has a defined return type, there are defined returns from messaging a nil object (e.g., methods that return an integer return 0 when messaging a nil object).
There are two things which can help here. The first is nil, the Objective-C equivalent of the Java NULL pointer - it can actually receive messages and respond to them. It will always return nil if the return value is an object, and 0 if the return value is some primitive type. Therefore if the Null behaviour of your object is "do nothing" you can easily just use nil as the Null value.
The other thing which is helpful is for when you need to store a placeholder or null value in a container object - these usually throw exceptions if you attempt to add nil as a value. Instead you can use the singleton +[NSNull null], which does nothing except act as a "this space intentionally left blank" object.
With these two weapons at your disposal there should be no reason to write a null instance of a custom class :-)
For your Response.YES and Response.NO, I assume you have instances that you do want to change, rather than just making all Response properties read-only.
A common pattern in Cocoa is to have both immutable and mutable versions of a class (NSArray versus NSMutableArray). For your response example, it would make sense to have an immutable Response class that has the static YES and NO methods, and a MutableResponse subclass that exposes setters for those times where you do want objects to change them. Does this cover your second example?
I don't think there is an easy way to provide this implementation. You're asking for something that is a language feature of Java to be implemented in Objective-C - you can do it but you have to write the code that is in the Java runtime yourself - there is nothing to stop you doing this but it's not something the language has built in.
It's a bit like asking "How do I show a Windows style 'one menu per window" UI in Cocoa' - you can do it but it's not provided for free from the framework. Or, "how can I easily implement Objective-C's nil pointer handling in Java?"
If you really want to see this type of functionality I think you should follow the NSArray/NSMutableArray design pattern. Declare a superclass that can handle all of your special cases:
#interface NullPizza : NSObject
{
}
- (BOOL)areYouANullPizza;
#end
and then subclass with your real Pizza and include a newNullPizza class method (which is just syntax sugar):
#interface Pizza : NullPizza
{
}
+ (Pizza*)Null;
#end
#implementation Pizza
+ (Pizza*)newNullPizza
{
return [[NullPizza]alloc init]; // Singleton code left as an exercise.
}
- (BOOL)areYouANullPizza;
{
return NO;
}
#end
Note that if you wanted to implement a +(Pizza*)NULL method on Pizza you should autorelease the new NullPizza you create.
Disclaimer, I typed this code straight into SO. I'd be surprised if it compiles but you get the idea.