Can you guys help me understand a concept real quick, I'm having trouble understanding the conversion from C to objective-C:
If I had a particular instance method that look like this:
-(void)addOwnerNamesObject:(NSString *)n;
{
// ownerNames defined as NSMutableSet
[ownerNames addObject:n];
}
I understand a few things...
It is an instance method that can be called by the program.
In C this would not return anything (just execute the code in the curlies)
In C, the syntax is slightly less confusing - (void)InstanceMethod(Char *nameOfArgument)
Here's where I need help:
When you call this method are you still sending it an argument?
If so, is that argument an NSString instance that the method names n?
And finally... off topic
If you have a method...
-(id)someMethod:(NSString *)pn
{
}
What is the (id) for? does that tell the compiler that it can return any type of object?
Thanks for helping the Newbie... Much appreciated.
First of all, you should really take a look at the basic Objective-C documentation.
In Objective-C, a method can be preceded by a + or - sign.
+ is for class methods, - is for instance methods.
Then you have the return type, inside parenthesis, and the method name.
- ( int )foo;
An instance method named foo, returning an int.
A similar C function would be:
int foo( void );
In Objective-C, the method name is a bit special when you have arguments.
For instance:
- ( int )foo: ( double )num;
A member method named foo:, returning an int and taking a double argument named num.
Similar C function:
int foo( double num );
Now with multiple arguments:
- ( int )foo: ( double )num1 bar: ( float )num2;
A member method named foo:bar:, returning an int and taking a double argument named num1 and a float argument named num2.
Similar C function:
int foo( double num1, float num2 );
About your question on id, it's simply the method return type.
id is a typedef used for Objective-C instances.
Basically, it's a void *.
id does represent an Objective-C object pointer, for any class.
You already know what you're talking about.
1.) When you call this method are you still sending it an argument?
yes, whatever is after the colon
add multiple colons to pass additional parameters...
-(void)addOwnerNamesObject:(NSString *)n withSomeIntYouWantToPass:(int)value;
2.) If so, is that argument an NSString instance that the method names 'n'?
yes
3.) What is the (id) for? Does that tell the compiler that it can return any type of object?
yes, you will return an NSObject or subclass of NSObject
First the dash (-) in the method name says that this is an instance method which means you need an instance to send this message to. The call would look something like this:
NSString* s = #"a string";
[someInstance addOwnersNameObject:s];
In this case you are passing the NSString instance s to the addOwnersNameObject message.
id is like void * in C.
To add to those very valid answers already given with a further discussion of id:
Objects in Objective-C are typeless, which means that at a fundamental level you don't need to know the type to be able to talk to the object. That's one of the big differences between Objective-C and, say, C++.
Pointers to objects are usually typed, such as NSString * to make the code more readable and to indicate your intentions to the compiler so that it can provide suitable warnings if you do anything odd.
id is a typeless pointer to an object. Any object type can be passed as id and any id value can be assigned to any object pointer without casting.
99.99% of the time, id could be replaced with NSObject * since 99.99% of objects inherit from NSObject, meaning that you could use the fact of inheritance rather than the fact of typeless objects to pass things around generically. However NSObject is a little bit special in being both an object and a protocol and some objects aren't actually subclasses of NSObject — NSProxy and the classes that represent blocks jump immediately to mind. You'll rarely be particularly interested in those special cases but id is nevertheless often used as a convention because people prefer the semantics of passing an object with no indication of its type to passing an object with a known ancestor.
Related
I understand that id is for any Object type even objects that do not inherit NSObject such as things from Cocoa. I have been told to almost always use id but what if I were making an API and had a method that I wanted to make it clear that it should only take a certain type of object such an object called Animal, would I still use
(id) animal
or would I do
(Animal) animal
Thanks so much!
id is a generic pointer to an object -- it's like void *, except that the pointer must point to an Objective-C object. So yes, you could use id in most situations where a more specific object pointer type would work, but it's usually better to use the more specific type:
- (id)animal; // OK if 'animal' could be any type of object
- (Animal*)animal; // much better if you know that 'animal' points to an object of type 'Animal'
You'll find plenty of examples if you look at any Cocoa or Cocoa Touch class. Let's look at a little bit of UIView:
- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
- (UIView *)viewWithTag:(NSInteger)tag; // recursive search. includes self
As you can see, the first method takes a UIView* as a parameter. If you try to pass something other than a pointer to an instance of UIView, the compiler will complain.
The second method returns a UIView*, and you can use the result directly as the receiver of other messages that UIView understands:
[[topView viewWithTag:someTag] removeFromSuperview];
Being specific about the types of parameters and return values lets the compiler help you make sure that you're sending appropriate messages to your objects and getting appropriate values back.
You can use any type starting from Animal and then up through inheritance chain to NSObject and id. Any would be valid. But in most cases you need to use just Animal because this is the very type you need to work with
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. :)
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
I am (trying to) learn Objective-C and I keep coming across a phrase like:
-(id) init;
And I understand id is an Objective C language keyword, but what does it mean to say "the compiler specifically treats id in terms of the pointer type conversion rules"?
Does id automatically designate the object to its right as a pointer?
id is a pointer to any type, but unlike void * it always points to an Objective-C object. For example, you can add anything of type id to an NSArray, but those objects must respond to retain and release.
The compiler is totally happy for you to implicitly cast any object to id, and for you to cast id to any object. This is unlike any other implicit casting in Objective-C, and is the basis for most container types in Cocoa.
id is a pointer to any Objective-C object (objc_object). It is not just a void pointer and you should not treat it as so. It references an object that should have a valid isa pointer. The values that can be stored in id are also not just limited to NSObject and its descendants, which starts to make sense of the existence of the NSObject protocol as well as the NSProxy class which does not even inherit from NSObject. The compiler will allow you to assign an object referenced by type id to any object type, assign any object type to id, as well as send it any message (that the compiler has seen) without warning.
id is a generic type. This means that the compiler will expect any object type there, and will not enforce restrictions. It can be useful if you're expecting to use more than one class of objects there; you can then use introspection to find out which class it is. id automatically assumes a pointer, as all objects in Objective-C are passed as pointers/references.
Some Additional Resources:
id vs NSObject vs id*
Objective-C Programming (Wikibooks)
Introspection
Dynamic Typing
id is a data type of object identifiers in Objective-C, which can
be use for an object of any type no matter what class does it have.
id is the final super type of all objects.
In java or c# we use like this
Object data = someValue;
String name =(Object)data;
but in objective c
id data= someValue;
NSString *name= data;
Yes and no. It's true that having id x designates x as a pointer, but saying that the pointer type conversion rules apply is wrong, because "id" has special type conversion rules. For example, with a void * pointer you can't do this:
void *x;
char *y = x; // error, this needs an explicit cast
On the contrary, it's possible with id:
id x;
NSString *y = x;
See more usage of type id in objective c examples.
In addition in the "modern" Objective C it's preferred to use instancetype instead of "id" on "init" methods. There's even an automatic conversion tool in Xcode for changing that.
Read about instancetype: Would it be beneficial to begin using instancetype instead of id?
take example:
-(void)setName:(NSString *)name age:(int)age;
How would you call this method (in other words, the method's name is setName but what is the "age" parameter doing in there) and what do the types in parentheses mean? Is it just a way to tell the compiler what types are being returned?
[ myObject setName: #"Adam" age:18 ];
The age parameter is the second parameter in the method signature.
The types in parentheses are the expected types for the argument. e.g. name is expecting only an NSString and age is expecting only an int.
The - means that the method is an instance method, not a class method, which is denoted using a + instead.
The type in parentheses right after the - is the return type.
This is a great site for learning the basics of Objective-C: CocoaDevCentral
To answer, one would need a bit more information, but I'll be guessing this is from some sort of class named aClass, and you have an instance of aClass, named instance.
-(void)setName:(NSString *)name age:(int)age;
means you have a method, named setName:age:, that needs two arguments, one NSString, one int, and it returns a void. As it has a - as it's first character, it is an instance method.
[instance setName:#"James Hargrove" age:21];
Would call setName:age: on the instance.
(The instance should be created using, say,
aClass *instance = [[aClass alloc] init];
which would create an instance of aClass named instance, and initialize it.
This is the standard Objective-C method syntax. This could be read as:
A method with no return value (void) that
sets the name of the object (an NSString * parameter)
and the age (and integer
parameter).
Dissecting the method:
"-" The hyphen states that this is an instance method.
(void) The return type is void - or
no return type expected
setName:(NSString *) The first
parameter to be passed is the "name"
and is an NSString *.
age:(int)age The second parameter
to be passed is the "age" and is
an int.
In reality, the method syntax is actually quite self-documenting once understood (and quite foreign if you're used to more tradition C/C++ or Java syntax).
The actual example of the call of this method would be:
[someObject setName:#"Rich" age:101];
The method name is actually this:
setName:age:
You call it like this:
[someObject setName:#"Alice" age:20];
setName:age: is also the unique signature of that method, and with that signature you can call that method on any object you wish. For example:
NSArray* objects = ...
SEL mySelector = #selector(setName:age:);
for (id object in objects)
{
if ([object respondsToSelector:mySelector])
{
[object setName:#"Alice" age:20];
}
}
what do the types in parentheses mean? Is it just a way to tell the compiler what types are being returned?
Yes, those are "C casts". If everything was an object you wouldn't need those, but because you can pass and return plain old C types to and from your methods, the compiler needs to know the types of your parameters and return values.
You'd call this method like so:
[classInstance setName:#"name" age:123];
The first instance of "age:" indicates that the method receives another parameter, called "age" when used in the implementation of the method.
The types in parentheses indicate the types of data that are expected for each parameter, with the exception of the first one, "void", which means that this method returns nothing.
So, you would call this method as follows.
Say it is a method of an object named foo (of class Foo). Then you would call:
[foo setName:someName age:someAge].
If it were a static method, it would be preceded by a + instead of a minus as follows:
+(void)setName:(NSString *)name age:(int)age;
Then you would call
[Foo setName:someName age:someAge] //use the classname instead of the object name
The types are indeed there for type-checking by the compiler. You'll get warnings if you pass the wrong sort of data, and you will get warnings if your header doesn't match your implementation.
You can actually write Obj-C functions in a couple of different styles though, omitting some of this stuff. You can even write straight up C-style.