What is the meaning of this Objective-C syntax? - objective-c

Would someone please clarify what the difference in these two snippets would be?
I know this is instantiation:
Class *myClass = [[Class alloc] init] ....etc
but what exactly is this?
(Class *)myClass .....etc
Thanks

The second snippet is either a cast or a parameter to a method. Neither have anything to do with instantiation.
If (Class *)myClass occurs in a method declaration, it just defines what type the parameter to the method should be. For example, - (void) method:(Class *)myClass is a method that returns void and takes one argument, of type Class*.
If (Class *)myClass occurs in other code, it's a cast. Basically it says to reinterpret myClass as a pointer to an object of type Class, regardless of what its type really is. It's like casting with numbers - if x is an int, (float)x casts it as a float so you can use it in floating-point arithmetic.
Generally speaking, I'd caution you against using casting heavily with Objective-C objects. One place you will see things like this is in casting NS objects to CF objects, as in (CFURLRef)[NSURL fileURLWithPath:path]. But most often objects of different types will not cast properly.
Also, you have an error in your first snippet. It would actually be [[Class alloc] init]. You must call alloc and then init. And [init] is meaningless - it doesn't fit the [object method] syntax of Objective-C at all.

The first one, given correct syntax is instantiating, as you say.
The second one is casting a variable "myClass" to a pointer to an instance of the Class object.

The second snippet is a C-style cast. It effectively tells the compiler to treat myClass as a value of type Class* regardless of its declared type. Without the rest of the snippet (and the preceeding declaration of myClass), it's impossible to say why you would want to use the cast or what effect it would have.

Related

Are selector IDs sensitive to argument types?

When you retrieve the ID of a selector with #selector(), is the selector value different depending on the types of the arguments?
Here's what I mean. I have a method that takes an object reference and a selector, then calls it with a parameter:
-(void)CallLater:(NSObject*) Obj Sel: (SEL)Sel
{
//Some stuff, then:
[Obj performSelector: Sel withObject: SomeOtherObject];
}
I'm using this method with a selector to a function that takes a typed object reference, not an id:
-(void)MyMethod: (MyObject*) a
{
}
[self CallLater: self Sel:#selector(MyMethod:)];
It seems to work, but my senses are tingling. In a statically typed language like C# this would be a foul, an upcast - CallLater is expecting a selector for a function that takes an id and I'm giving it a function that takes a MyObject.
On the other hand, the compiler does not complain, and both id and concrete object references seems to be mere pointers deep down, trivially castable to one another. Then again, there are many fouls that Objective C compiler does not complain about.
So the real question is - is it safe? Language lawyers welcome.
It's safe; objects are objects. A selector parameter for an NSObject * is exactly the same as a selector parameter for a MyObject *.
If you want MyMethod to verify that it's being called with an object of a particular type, it should do a NSParameterAssert on it:
NSParameterAssert([obj isKindOfClass: [MyObject class]]);
Personally, I rarely do this check. It's enough that the actual object acts like the type I want it to be, and if it doesn't I'll get a runtime error (usually unrecognized selector). You'll get a compiler warning in the simple cases, and it's worth paying attention to this warning (and silencing it with an id cast when necessary).
I'm a bit confused here about your use of id in your question, so I want to make sure you understand this: An NSObject * is exactly as much an id as a MyObject * is. id is a generic instance pointer class, whereas NSObject * is a NSObject instance (or a subclass of NSObject). You can have objects that don't descend from NSObject. But you're unlikely to ever have to know this.
Other notes, re: convention:
Selectors (both the name and parameters) start with lowercase letters, so CallLater:Sel: should be callLater:sel:.
Variable and parameter names start with lowercase letters; Obj above should be obj.
Class names do start with an uppercase letter. :)

Would it be beneficial to begin using instancetype instead of id?

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

Is casting ever required for the object on an NSNotification?

When I receive an NSNotification do I ever need to cast notification.object? Suppose I know notification.object will be an instance of MyClass and I do the following:
MyClass *myClass = notification.object;
Is any casting necessary here? How is the above assignment different from:
MyClass *myClass = (MyClass *)notification.object;
No, it is entirely unneccessary and does not change anything about the behavior of your program. Casting only happens at compile time, and in the case of pointers, is just used to assure the compiler that you know what type the object is.
The compiler may complain about an assignment if you are, for example, setting a variable of type Subclass to the result of a method that returns type Superclass, where you know that the actual object you are going to get back is of type Subclass. In that case, you would cast to the subclass. E.g.,
MyViewController * vc = (MyViewController *)[someWindow rootViewController];
The type of notification.object is id, a generic object pointer, and the compiler is perfectly happy to assign such a pointer to any other type of pointer.
No. Objective-C doesn't require a cast from type id to another object type.

Should I be casting when returning id from an objective-c method or not?

For the Objective-C gurus:
Suppose I have a simple method like so:
-(id)getValue{ return [NSNumber numberWithDouble:5.0]; }
Now, suppose within some other method I call the (id)getValue method like so:
NSNumber* myValue = [self getValue];
or what if I call it like this instead:
NSNumber* myValue = (NSNumber*)[self getValue];
The question is: Obviously these lines are equivalent but one of them utilizes an explicit cast. So what is the correct or best-practice way of doing this. It seams to me the cast is unnecessary since when it is placed in the pointer myValue, it will be type-safe at this point anyways (which is something I want) so the cast is basically pointless.
Let me just add that I'm sure people will point out: Why don't you just return (NSNumber*) from the getValue method but in my case I want to have the flexibility to return whatever I want much like the built in NSDictionary class returns id when you call: objectForKey because it allows you to place any type of NSObject or subclass inside of it. In other words my getValue method will not always be returning an NSNumber. Also consider this example is contrived because I am just concerned about whether to cast or not.
Thank you in advance,
-Ralph
The only reason to cast objects is to make the compiler happy. (Sometimes it also helps readability.) For example, you have to cast when making a property access directly on an object you're getting out of an array or dictionary:
((Foo *)[myArray objectAtIndex:0]).bar;
If you don't do the cast, the compiler can't do the property lookup, and will complain.
When you're getting an object from a method that returns id, it's impossible for the compiler to know what its actual type is. There isn't really any "type-safety", because id is a generic pointer; all the compiler can and will enforce is that the method says it returns some Objective-C object. It is perfectly happy to assign a generic pointer to any typed pointer.* (This is actually an advantage for containers, obviously.) Since the type of the variable to which you're assigning already documents the actual return type, I'd say there's no need for the cast.
As an aside, you shouldn't be calling your method getX. That has a specific meaning in Cocoa; methods which "get" something pass in a pointer to a pointer, which is then filled by the method. See -[NSArray getObjects:range:] as an example.
*The type will be enforced at run-time, of course, in the sense that sending messages to which the object does not respond will cause an error.

Incompatible Type for Argument 1 Objective-C

I have a function that sets an entity within a Core Data store. I used to have all values it would be storing as type double, however now I must make it accommodate NSStrings as well. Consequently, I changed the type of the parameter the function takes in, to an id type. However, now I get the error:
error: incompatible type for argument 1 of 'numberWithDouble:'
...at the following lines:
//...
[dfm setTimeStamp:[NSNumber numberWithDouble:value]];
//...
[[fetchedObjects objectAtIndex:0] setValue:[NSNumber numberWithDouble:value] forKey:#"timeStamp"];
//...
Apparently it doesn't like the [NSNumber numberWithDouble:value] segment of each line. I was contemplating making a container class that holds an NSNumber type (doesn't Apple already have a class like this?) to get around this problem, but I thought that there has to be an easier way I am not thinking of (besides duplicating the function and changing the type of the value parameter). Any ideas? Thanks in advance!
EDIT:
Here is the function declaration:
-(void)setItemInDFMWhilePreservingEntityUniquenessForItem:(attribute)attr withValue:(id)value
attribute is merely an enum which specifies which entity to store within. The problem is that the compiler is giving me problems with value being of type id, theoretically I can pass in anything I want, and I believe the way I have it I am implying that I will be passing it as an NSNumber, but the compiler doesn't like that as that is not actually a class instance I suppose?
The problem is that the compiler is
giving me problems with value being of
type id, theoretically I can pass in
anything I want, and I believe the way
I have it I am implying that I will be
passing it as an NSNumber, but the
compiler doesn't like that as that is
not actually a class instance I
suppose?
By declaring value as id, you can pass any object you want. But why do you "suppose" that NSNumber isn't an object, when it's clearly documented as being an object? The warning isn't about passing an NSNumber instance when you've declared value as an id - that's perfectly valid, because id means "any object," and an NSNumber instance is an object. The warning comes from calling +numberWithDouble:, a method that takes a double for its first argument, and passing it value, which is declared as id - i.e. an object. You can't pass an object to a method that expects a double.
Your proposed solution, typecasting value with (NSInteger)value will silence the warning, but it won't fix the problem. The typecast simply converts the memory address the object pointer targets to an integer value. If (as your edit suggests) value is already an NSNumber object, what do you hope to gain by creating another one, or by typecasting its memory address to an integer? Just do:
[dfm setTimeStamp:value];
The problem lies with the value variable. It should be declared as a double (primitive) for this call to succeed.
edit: after rereading your question, do a check in the function on the type of value, if it is an NSString (use [value isKindOfClass:[NSString class]]) store it as such, if its not then its a double (if thats the only two types you are passing) and store it as such.
Can't you just pass the NSNumber instead of double?
Just realized that the call I was making (numberWithDouble:) was having the compiler check for a primitive, i.e. double. Changing it to the following worked like a charm:
[dfm setTimeStamp:[NSNumber numberWithInteger:(NSInteger)value]];
Thanks to those that responded!