Why the compiler gives an error in this case of method declaration -
-(void) someMethod (void);
But approves this -
-(void) someMethod;
(SomeClass.h)
I've read that it is better to declare (void) in parameters than not declaring, but probalby I miss some point.
You cannot do this for Objective-C.
In Objective-C, every parameter must be after : e.g.
- (void)someMethod:(int)i;
- (void)someMethod:(int)i withString:(NSString *)string;
- (void)someMethod:(int)i :(int)i2 :(int)i3; // you can do this but is bad style
and it does not make sense to make something like
- (void)someMethod:(void)what_goes_here;
so if you want a method without parameter:
- (void)someMethod;
However you can do it in C/C++
void someMethod(void);
And I didn't see any benefit of declare void parameters (explicitly declare things is not always good).
Expanding on #xlc's answer
The answer is a difference in syntax between Objective-C and "normal" C/C++.
Way back in the origins of Unix and C days, late 60's/early 70's, when declaring (not defining) a function, you didn't need to specify how many arguments it accepted, or what types they needed to be. Nor did you need to specify whether or not it returned a value.
Later, folks realized that this would be a good idea, both for better error detection at compile time and for greater efficiency of generated code. So implementers added the ability to specify argument types in the function declaration. This was standardized as part of ANSI C in the late 80's.
However, there was a need to retain backwards compatibility with existing code. So a function declaration of foo() couldn't be assumed to mean "function with no arguments". To solve this, the void keyword was introduced. That allowed you to say foo(void) to mean "function named foo which takes no arguments".
When Objective-C was invented in the 90's, they added a new syntax for defining methods. Because there was no legacy code to deal with, they simply said that a method has to declare all its arguments; if there are none, then the method takes no arguments.
Objective-C still uses the void keyword to indicate that a method returns no value, though.
Related
During a talk at the #Scale 2014 conference (around 32:30), Facebook presented their implementation of a declarative UI approach. The slides for a more detailed version of the talk can be found here.
Basically they presented a function call like this (I made my own simplified example from the example in the talk):
[CPInsetComponent
newWithStyle:{
.margin = 15
}
];
My question is: Is this valid ObjC code? I tried to implement this myself
typedef struct {
uint margin;
} CPInsetComponentStyle;
#interface CPInsetComponent : NSObject
+ (SomeOtherStruct) newWithStyle:(CPInsetComponentStyle)style;
#end
but I still get an "expected expression" error on the newWithStyle:{ line. Could you give me a hint how the method declaration would look like?
No, that's not valid Objective-C code. A C99 compound literal of struct type looks like this:
(TheStructType) { .field1 = initializer1, .field2 = initializer2 }
where the field designators are optional.
I can imagine that the code they were presenting was actually Objective-C++. In C++11, the compiler can insert implicit calls to constructors taking an initializer list if certain conditions are met; hence, often you can pass just an initializer list to a function.
The compiler probably doesn't know if your literal struct declaration is of the correct type. For compound literals you need to provide the type in parenthesis followed by a brace-enclosed list of initializers.
[CPInsetComponent newWithStyle:(CPInsetComponentStyle){
.margin = 15
}];
__attribute__((const)) is a GCC attribution to check purity of the function execution.
I think this is being supported on Clang, but when I put this to a method, it doesn't seem to work.
#interface C1
- (id)method1 __attribute__((const));
#end
int a = 0;
#implementation C1
- (id)method1 __attribute__((const))
{
a++;
return nil;
}
#end
The code above doesn't generate any warning or error.
Is this attribution working on Clang? Or what should I do to make this to work?
The const attribute doesn't check the purity of the function. It declares the purity of the function, so the optimizer can eliminate calls to the function. It's up to you to actually make sure the function is pure.
Declaring a method const can not work in Objective-C because it contradicts the runtime's message passing mechanism. The documentation for const says:
Note that a function that has pointer arguments and examines the data pointed to must not be declared const.
Every Objective-C method has at least one (implicit) pointer argument: self. It is always examined as part of message passing (objc_msgSend()). That's what makes Objective-C dynamic.
In ObjC we can use protocols to restrict an id behavior, so we can declare something like
-(void)aMethod:(id<aProtocol>)aVar which works very well until we provide a value or a non-id variable as aVar, but this gets completely broken since we can pass a generic id variable delcared without protocols specifiers... Is this normal? Is there any workaround? Am I missing something?
Just use id less, and declare variables and parameters using the correct types, where possible. That is to say: don't pass ids around. If you are implementing a collections class (for example), then id's often useful.
My approach is to specify types, and introduce that type as local as possible in the source. So I omit id and add the type, and when (for instance) I take a reference from a collection, I create a variable:
MONType<MONProtocol>* thing = [array objectAtIndex:idx];
// now thing is correctly typed. use thing.
Similarly, if I have an id parameter, I declare a new variable:
- (IBAction)someAction:(id)sender
{
NSButton * button = sender;
// now use button, not sender
Protocols are extremely useful. Very often, better/cleaner than subclassing.
You're missing the understanding that types in Objective-C are determined at runtime, not compile time. Just because you say that an object will be of type id<aProtocol> does not mean that at runtime it is guaranteed to be so.
The idea of specifying something as id<aProtocol> is to aid you as a developer and people using your code. It aids you as a developer because the compiler will warn (or error under ARC) if you attempt to call a method on something that the compiler can determine it doesn't think exists on instances of its supposed type (excluding forwarding which could mean an instance responds to something the compiler cannot determine). It aids people using your code as it tells them the contract that they should adhere to when interfacing with your code.
So, in your question you say that:
but this gets completely broken if we pass a generic id variable delcared without protocols specifiers
Well, the compiler would warn and tell you that you're trying to pass something that does not conform to that protocol, except for the case of passing id. That's why you generally should try to type things more precisely than just id.
If you have a method defined like so:
- (void)aMethod:(id<aProtocol>)aVar
Then aVar could be of type SomeSubclass where that is defined like so:
#interface SomeSubclass : NSObject <aProtocol>
And you could then use aMethod like this:
SomeSubclass *obj = [SomeSubclass new];
[other aMethod:obj];
I (FINALLY) found out that using Objective-C++ is the way to go. Let's suppose I want to be able to pass NSString or NSNumber (instead of a too much generic id and instead of using protocols which become useless passing id values): well, I can create a C++ class having two distinct constructors, one for each ObjC class, so passing id values cannot be done anymore (almost directly). For example, let's take a look at
class NSStringOrNSNumber{
public:
NSStringOrNSNumber(NSString *);
NSStringOrNSNumber(NSNumber *);
};
The great advantage is that methods/functions taking a NSStringOrNSNumber parameter can get NSString/NSNumber values DIRECTLY, since the constructor acts as an implicit cast. In other words, if we have
void aFunction(NSStringOrNSNumber param);
the following calls are perfectly valid:
aFunction(#"Hello!");
aFunction(#25);
The only (little) downside is that we need the class to implement a function if we want to get back the value passed to the constructor.
Using a C++ class constructor to get something like id<NSCoding> is still better the using id<NSCoding> directly: in fact, if we do the following
#class classOne, classTwo;
class NSCodingClass{
private:
NSCodingClass(classOne *);
NSCodingClass(classTwo *);
public:
NSCodingClass(id<NSCoding>);
}
we won't be able to pass a generic id as a parameter (since it would be ambiguous: the compiler cannot know which constructor to call among the two private ones)
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've tried to use id to create duck typing in objective-c. The concept looks fine in theory but failed in practice. I was unable to use any parameters in my methods. The methods were called but parameters were wrong. I was getting BAD_ACESS for objects and random values for primitives. I've attached a simple example below.
The question:
Does any one knows why the methods parameters are wrong?
What is happening under the hood of the objective-c?
Note: I'm interest in the details. I know how to make the example below work.
An example:
I've created a simple class Test that is passed to an other class using property id test.
#implementation Test
- (void) aSampleMethodWithFloat:(float) f andInt: (int) i {
NSLog(#"Parameters: %f, %i\n", f, i);
}
#end
Then in the class the following loop is executed:
for (int i=0; i < 10; ++i) {
float f=i*0.1f;
[tst aSampleMethodWithFloat:f andInt:i]; // warning no method found.
}
Here is the output that I'm getting. As you can see the method was called but the parameters were wrong.
Parameters: 0.000000, 0
Parameters: -0.000000, 1069128089
Parameters: -0.000000, 1070176665
Parameters: 2.000000, 1070805811
Parameters: -0.000000, 1071225241
Parameters: 0.000000, 1071644672
Parameters: 2.000000, 1071854387
Parameters: 36893488147419103232.000000, 1072064102
Parameters: -0.000000, 1072273817
Parameters: -36893488147419103232.000000, 1072483532
Update:
I've found out by accident that when I add a declaration of aSampleMethodWith... to the class with for loop the warning disappears and the method on the Test class is called correctly.
Update 2:
As pointed out by JeremyP the direct cause of the problem is that the floats are treated as doubles. But anyone knows why? (following the 5why principle :) ).
According to #eman the call is translated to simple C function call and compiler directive to get the SEL. So the #selector gets confused. But why? The compiler have all necessary type informations in the first method call. Does any one knows a good source of information about the Objective-C internals I've search The Objective-C Programming Language but i didn't find the answer.
By default floating point values are passed as doubles, not floats. The compiler does not know, at the point where [tst aSampleMethodWithFloat:f andInt:i]; occurs that it is only supposed to pass a float, so it promotes f to a double. This means that, in the method, when the compiler does know it is dealing with a float, f is the float formed by the first four bytes of the double passed to the method and i is an int formed from the second four bytes of the double passed.
You can fix this by either
changing the first parameter of aSampleMethodWithFloat:andInt: to a double
importing the interface declaration of Test into the file where you use it.
NB there is no gain except a small amount of space when using floats in C. You might as well use doubles everywhere.
I think JeremyP is correct about the problem being about doubles vs floats. As for implementation details, message dispatch in Objective-C uses the objc_msgSend(id theReceiver, SEL theSelector, ..) C function (for some deep nitty-gritty, see here). You can simulate the same results of method dispatch like so:
SEL theSelector = #selector(aSampleMethodWithFloat:andInt:);
objc_msgSend(self.test, theSelector, 1.5f, 5);
SEL is just a number that corresponds to a function (that is dynamically determined based on the method signature). objc_msgSend then looks up the actual function pointer (of type IMP) of the method and invokes it. Since objc_msgSend has a variable number of arguments, it will just use as many as you pass in. If you were to do:
objc_msgSend(self.test, theSelector, 1.5f);
It would use 1.5f correctly and have junk for the other variable. Since the method signature typically denotes the number of arguments, this is hard to do under normal usage.
You can make the warning go away by making a category like this:
#interface NSObject (MyTestCategory)
- (void) aSampleMethodWithFloat:(float) f andInt: (int) i;
#end
Without a signature available at the calling point, it isn't known what type the parameters are supposed to have. Undefined methods will be assumed to take ... as parameters, which isn't what yours does. If there is any interface seen by the compiler at this point, where the method in question exists, that definition will be used.
The trouble here is with the dividing line between C and Objective-C. The id type specifies any object, but ints and floats are not objects. The compiler needs to know the C type of all the arguments and the return type of any method you call. Without a declaration, it assumes that a method returns id and takes an arbitrary number of id arguments. But id is incompatible with int and float, so the value doesn't get passed correctly. That's why it works correctly when you provide a declaration — then it knows your int is an int and your float is a float.