I have a method that is declared as follows in Objective-C
+(void) clearAll:(NSArray<ParameterType> *)values;
I have another class method that is declared as follows, in another object
+(NSArray *) values;
I am trying to call these from Swift as follows:
MyObject.clearAll(MyOtherObject.values())
It displays an array of different compile-time errors when I try to run it.
The errors are:
"Generic parameter 'ObjectType' could not be inferred"
then I try MyObject.clearAll(MyOtherObject.values() as NSArray<MyOtherObject>)
and get "Cannot specialize non-generic type 'NSArray'"
When I try MyObject.clearAll(MyOtherObject.values() as! Array)
I get "Array<MyOtherObject> is not convertible to [Any]"
How can I fix this?
The compiler errors are exceedingly unhelpful in this situation; they point pretty much everywhere except the right place. The problem is because of the generic parameter on MyObject: in Swift, an unspecialized generic type is not a thing unto itself, and methods can't be called on it. It has to be specialized to become concrete.
The resolution is simple: just add the type argument:
MyObject<MyOtherObject>.clearAll(MyOtherObject.values())
(You'll need to do the same for MyOtherObject if it's generic too.)
Related
the Code: [NSClassFromString(#"Test") gotoTest];
usage ARC with Error: No known class method for selector 'gotoTest'
but in MRC warning no error.
Way ARC from warning becomes an error ? do you have any reference? I want to know the essential reason.
You did not include the warning under MRC:
Class method '+gotoTest' not found (return type defaults to 'id')
and this contains a vital clue – the compiler is looking for the return type. Under MRC it assumes id and will let you assign the result as an object reference. If you mess up and the return type is, say, float, things will probably go wrong.
Under ARC it is the compilers job to do the memory management of any returned value, and to do this correctly it needs to the the type. So if it can't determine the return type it produce an error.
Your code fragment suggests you know the selector takes no arguments and returns no value, so declare it as such. If you've no class with the method you can use a protocol, something like:
#protocol GotoTest
+ (void) gotoTest;
#end
will do. Now the compiler knows the types and your code will compile under ARC.
HTH
In my implementation model, I need to have some differents files, with some similar methods. Also, I gather objects of differents types in a NSMutableArray, and try to call my similar method like this:
[[ArrayOfViews objectAtIndex:i] myMethodWithInt:'number' andExample:'char'];
But it gives me a warning:
Instance method '...' not found (return type defaults to 'id')
My code works, but should I fix this warning or not?
You should introduce the type:
MONType * obj = [ArrayOfViews objectAtIndex:i];
Then call the method using the typed variable:
[obj myMethodWithInt:<#number#> andExample:<#char#>];
It could be complaining for a number of reasons. Introducing the type will either fix those categories of issues, or at least give you a more useful warning. Specifically, the compiler does not see a declaration of the instance method -myMethodWithInt:andExample:.
You've likely either not imported a header, or declared the method so that it is visible in the translation.
My code works, but should I must fix this warning or not ?
Maybe. Sometimes it will 'work'. This is equivalent to C89's missing function prototypes, which were phased out because of all the issues they caused. So implicit parameters and return types can work if the parameters and return type is id compatible (including NSObject subclasses). But if it is not a match, then you should expect undefined behavior. You can avoid all this and introduce meaningful type checking by using the appropriate imports and declarations in your programs, and by introducing the type as illustrated above. This is useful because the compiler will save you from a ton of silly mistakes as you work and as your codebases evolve.
It's probably just a case of not having the method declared before the call. Try adding the method declaration to the interface section of the class.
Adding a typecast to your objectAtIndex call with get rid of the warning. This isn't a big issue, but it's good practice to typecast returns from an NSArray.
Basically:
YourObjectType *yourObject = (YourObjectType*)[ArrayOfViews objectAtIndex:i];
[yourObject myMethodWithInt:'number' andExample:'char'];
You need to add an #import statement for the header that declares the method or methods you want to call.
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 have the following declaration in my class called GameManager:
-(void)playBackgroundTrack:(NSString*)trackFileName isAmbient:(BOOL*)ambient {
}
I want to be able to call
[[GameManager sharedGameManager] playBackgroundTrack:BACKGROUND_TRACK isAmbient:NO];
or
[[GameManager sharedGameManager] playBackgroundTrack:BACKGROUND_TRACK isAmbient:YES];
but I am getting a warning:
Semantic Issue: Instance method '-playBackgroundTrack:isAmbient:' not found (return type defaults to 'id')
and trying to use the isAmbient parameter crashes.
it works ok if I leave the isAmbient declaration off in both places (but of course I can't use the parameter I want!).
You should use plain BOOL, not BOOL* in the declaration, like this:
-(void)playBackgroundTrack:(NSString*)trackFileName isAmbient:(BOOL)ambient;
Change the parameter type of isAmbient from BOOL* to BOOL.
The first piece of code is not a declaration, it's a definition. Declarations are signatures, end with a semicolon and are placed in header files (generally).
You need to then include that class header file with the declaration, so your other class knows the method exists. This will solve the "method not found" issue.
By the way, you probably meant to use a plain BOOL, not a pointer to it. This might be the cause of the crash.
I have a class that has two classes A and B added to it. In a method in class A, I am trying to call a class B method
Let's assume that the parent class is debugZoneScene, debugZoneLayer is class A and tetraCounter is class B.
Here is a method from debugZoneLayer (class A):
-(void) getHeroVel {
DebugZoneScene *debugZoneScene = (DebugZoneScene*)self.parent;
[debugZoneScene.tetraCounter setTetras];
}
It calls the method, but I get the warning:
'-[DebugZoneLayer getHeroVel]':
'CCNode' may not respond to '-setTetras' (Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments.)
I've tried Googling this, but I couldn't really find anything that related exactly to my problem. I am using Cocos2D, but I think this problem doesn't have anything to do directly with that, and can still be resolved having knowledge in Objective C. Any ideas?
The compiler is telling you that it thinks that debugZoneScene.tetraCounter is an object of type CCNode, not whatever your ClassB is. Check how tetraCounter is declared and allocated in DebugZoneScene.
You can make the warning go away by casting:
[(ClassB *)(debugZoneScene.tetraCounter) setTetras];
this tells the compiler that you don't care what it thinks and you're sure that the object is ClassB. This doesn't solve the actual problem, however.
Your pseudo really fits you... without more details about the signature of setTetras, it will be quite difficult to guess what is wrong in your code ^^
Anyway did you #import the header for TatraCounter class declaration, so that the file where you wrote this code knows about the methods available (and their signature) for the TetraCounter objects?