Using a selector to call a method that exists in more that one class (but with different signatures on return or argument types) causes an Multiple methods named [method name] found... error.
This has been already settled in other questions:
Defeating the "multiple methods named 'xxx:' found" error
Issue with "Multiple methods named...."
Multiple methods named "count" found with mismatched result, parameter type or attributes
A related problem happens if the repeated method is in a protocol. Having a strongly typed protocol object is still ambiguous for the compiler since the object could also be an instance of other classes implementing the same-ish signature method:
#protocol ExistingMethodProtocol <NSObject>
// This method also exists in UIView, but with a different return type
- (NSString*)contentMode;
#end
#interface ImplementingClass : NSObject <ExistingMethodProtocol>
#end
#implementation ImplementingClass
- (NSString*)contentMode {return nil;}
- (void)problematicCall
{
// Multiple methods named... error
[self performSelector:#selector(contentMode)];
id<ExistingMethodProtocol> idOfProtocol = self;
// Multiple methods named... error too, even casted
[idOfProtocol performSelector:#selector(contentMode)];
}
#end
An alternative is to create the selector separately and then perform, thus bypasing the compiler check but causing a warning:
SEL selector = #selector(contentMode);
// Warning: PerformSelector may cause a leak because its selector is unknown
[object performSelector:selector];
What other alternatives could work here for checking and performing a method when a protocol has a method colliding with a same-ish signature?
my 2 cents. Use Runtime reference. In which case it bypasses the compiler warnings/error. Looks like there are several ways to do this, here is my solution.
#import <objc/message.h>
struct objc_method *method = class_getInstanceMethod([self class], #selector(contentMode));
id answer = method_invoke(self, method);
This should prevent errors from selectors in different protocols named the same. But I am a little unsure how this behaves with methods with different implementations in base and super classes.
Related
Pretend I have a category on NSObject that defines the following method:
+ (instancetype)allocTemplate
{
id instance = [self new];
return instance;
}
and I have the following class:
#interface FDActor : NSObject
#property (nonatomic, copy) NSString *name;
+ (void)sayHi;
#end
#implementation FDActor
+ (void)sayHi
{
[self allocTemplate].name;
}
#end
How come [self allocTemplate].name errors out at compile time if self is FDActor?
I am aware it works if you use normal message sending syntax but I am explicitly interested in the dot syntax error.
It would appear as though instancetype is used only for type checking during assignment, so
FDActor *actor = [FDActor allocTemplate] would not produce a warning.
If we cast the return type of allocTemplate the problem goes away.
- (void)sayHi
{
((__typeof__(self))[[self class] allocTemplate]).name;
}
But note that this only works in an instance method since the typeof an instance is another instance. Note also that since we now explicitly type the return value the allocTemplate method is no longer necessary, if all were looking for is type checking then we can even just cast nil and it will work.
If we try the same thing in a class method it doesn't work
+ (void)sayHi
{
((__typeof__(self) *)[self allocTemplate]).name;
}
This is because (__typeof__(self) *) doers not evaluate to FDActor * but Class * which ARC will complain about. It looks like there is no way to resolve the type information FDActor * in a generic way from within a class method at compile time.
I rather expected the instancetype keyword to be a little more useful.
The error message tells you. [self allocTemplate] is an id. Property syntax never works on id.
Note that this does work:
[FDActor allocTemplate].name;
I think the class to which instancetype is to be related must be specified in the call; otherwise we just fall back on id.
I'll take a stab at this, with the disclaimer that I don't fully understand how instancetype support is actually implemented, let alone all the nitty gritty details of the compiler's type checking system. In other words, this is my best guess, and not an authoritative or complete answer...
After compilation, Objective-C methods are actually functions, where self is the first argument to the function, and is typed id. So you have:
void sayHi(id self, SEL _cmd)
{
[self allocTemplate].name; // Actually calls to objc_msgSend, but that doesn't matter here
}
The compiler's treatment of the type of value returned by +allocTemplate here seems to be related to this. If you change self to an explicit FDActor the error goes away. Of course, for methods -- as opposed to properties -- the compiler types self as you'd expect, and will warn (or error under ARC) for methods that self doesn't appear to respond to. It seems like this is perhaps a difference (bug?) in the compiler's checking for available methods vs. properties in the context of instancetype.
I have this piece of code:
#interface Bar : UIView
- (id)initWithInt:(int)i;
#end
#implementation Bar
- (id)initWithInt:(int)i {
self = [super init];
return self;
}
#end
void func() {
[[[Bar alloc] initWithInt:10] doSomething];
}
The compiler gives me an error in the func() function: No visible #Interface for Bar declares the selector doSomething.
I do have another #interface with the method doSomething.
The compiler somehow assumed that initWithInt: returns instancetype rather than id.
What are the rules to this? In what cases does a compiler assume that a method returns instancetype?
In what cases does a compiler assume that a method returns
instancetype?
That is documented in "CLANG LANGUAGE EXTENSIONS":
According to Cocoa conventions, Objective-C methods with certain names
(“init”, “alloc”, etc.) always return objects that are an instance of
the receiving class’s type. Such methods are said to have a “related
result type”, meaning that a message send to one of these methods will
have the same static type as an instance of the receiver class.
...
To determine whether a method has an inferred related result type, the first word in the
camel-case selector (e.g., “init” in “initWithObjects”) is considered,
and the method will have a related result type if its return type is
compatible with the type of its class and if:
the first word is “alloc” or “new”, and the method is a class method,
or the first word is “autorelease”, “init”, “retain”, or “self”, and
the method is an instance method.
So your initWithInt: method has a "inferred related result type", and
therefore the compiler assumes that it returns an instance of Bar.
don't know BUT
inits always should return instance types anyways. it is only logical. I cannot imagine any other situation.
Just import the subclass that declares doSomething and you're good
I do have another #interface with the method doSomething.
Each class must have exactly one #interface (although you can make class extension, categories, etc. to add further methods). If your -doSomething method is in a category (or class extension) in another file, make sure that its declaration is visible in this file via an #import.
I have a class that essentially acts as a light weight wrapper class around another class. It holds that other class as an iVar. I want to be able to expose certain properties (quite a few actually) of the iVar, but to do so I have to write out each property accessor like so:
- (void) setProperty:(Class *)value{
_iVar.property = value;
}
- (Class *) property{
return _iVar.property;
}
Of course, I have to do this for every single property, which is a pain (there are about 30 of them). I would love to be able to synthesize this but I haven't been able to figure out how.
Is it possible to synthesize?
Also, I can't subclass....well, I might be able to but it's really not recommended. The iVar class is really quite heavy (it implements CoreText). I'd rather write out the methods by hand.
Ok, so here's the solution I found...ended up being pretty simple once you knew what to do. First overwrite '- (id) forwardingTargetForSelector:(SEL)aSelector' and return the iVar:
- (id) forwardingTargetForSelector:(SEL)aSelector{
return iVar;
}
When the runtime is looking for a method and cannot find one, it will call this method to see if there is another object to forward the message to. Note that this method normally returns nil and if you return nil here, your program will crash (which is the appropriate behavior).
The second part of the problem is to shush the compiler errors/warnings you'll get when you try to send a message that's not declared. This is easily done by declaring a category you don't implement.
#interface Class (iVarClassMethods)
#propoperty (strong) Class *property1;
......more properties
#end
As long as you don't put in an implementation anywhere, aka #implementation Class (category), the compiler won't complain (it'll assume that the implementation is somewhere....).
Now the only drawback I see is if you change any of the properties in the interface of the iVar Class, you need to make sure you update all other classes that use the method described above, otherwise you'll crash when another class tries to send what is now the wrong method (and the compiler won't warn you beforehand). However, this can be gotten around. You can declare protocols in a category. So instead you create a separate protocol for the iVar class and move the methods/properties you wish out of the iVar class into the protocol.
#protocol iVarClassProtocol
#propoperty (strong) Class *property1;
......more properties
#end
Add that protocol to the iVar subclass so it has those methods declared through the protocol now.
#interface iVarClass <iVarClassProtocol>
....other methods/properties you don't need forwarded
#end
Finally, simply add the protocol to the category. So instead of the aforementioned category with explicit declarations you'll have:
#interface Class (iVarClassMethods) <iVarClassProtocol>
#end
Now, if you need to change any of the to-be-fowarded properties/methods, you change them in the protocol. The compiler will then warn you when you try to send the wrong method to the forwarding class.
I think you can forward the messages to the ivar:
- (void) forwardInvocation: (NSInvocation*) invocation
{
[invocation invokeWithTarget:ivar];
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *our = [super methodSignatureForSelector:selector];
NSMethodSignature *ivars = [ivar methodSignatureForSelector:selector];
return our ? our : ivars;
}
Then you have to hide or fake the type of your object, for example by casting to id, otherwise the compiler will complain that your class does not implement those methods.
Of course it would be best if you could come up with some better design that would do without such tricks.
I am just curious about whether declaring a variable in a way known from Java is possible in Objective-C:
Class<?extends SomeType>
For example: I have a class called MyClass. It has a static method
+ (void)myMethod
It has also two subclasses: MySubclassA and MySubclassB. I have such code:
Class myClass;
if(<some condition>) {
myClass = [MySubclassA class];
} else {
myClass = [MySubclassB class];
}
[myClass myMethod];
This code works fine, there are no compiler warnings, but I am just curious whether the construction I mentioned is somehow present in Objective-C.
Thanks!
Objective-C does not have templates (like C++) or generic types with type erasure (like Java) or runtime generic types (like C#). Unlike these languages, Objective-C messages are dynamically dispatched at runtime (instead of bound at compile time). Thus, many of the systems for producing type-agnostic code in C++, Java or C# are unnecessary. Objective-C prefers "duck-typing" whereby any object that responds to a given selector (message) can be given that message by calling code, regardless of the receiving object's type. Since classes are objects in Objective-C, the same is true for class methods as for instance methods.
So, given
#interface MyClassA : NSObject
{}
- (void)someMethod;
#end
#interface MyClassB: NSObject
{}
- (void)someMethod;
#end
calling code can look like this
- (void)someOtherMethodInAnOtherClassWithObject:(id)obj
{
[obj someMethod];
}
This code will compile and will work fine at runtime, assuming obj is either an instance of MyClassA or MyClassB.
Of course, good practice would dictate that you define a #protocol in this situation:
#protocol MyProtocol
- (void)myMethod
#end
and declare that your MyClassA and MyClassB both implement the MyProtocol protocol. Your calling code would then look like
- (void)someOtherMethodInAnOtherClassWithObject:(id<MyProtocol>)obj
{
[obj someMethod];
}
and the compiler would give you a warning/error (depending on -W flags) if you tried to call someOtherMethodInAnOtherClassWithObject:, passing an object of a type that doesn't implement the MyProtocol interface.
Note that id<MyProtocol> is not a generic type, it's an instance of type id that you are claiming implements the MyProtocol protocol. Also note that the first version of client code works just fine because all that really maters is whether obj can respond to the -myMethod selector.
I've been searching around Apple's delegation and protocol documentation for an answer to this, but after more than a day I've decided to give up and let you guys have a shot at it. I have three classes: HTTPManager, LoginManager, and FetchManager. You can probably guess what these classes do, but to be explicit...
HTTPManager - Wraps NSURLConnection and provides a simple interface for LoginManager and FetchManager to do HTTP requests with authentication.
LoginManager / FetchManager - Basically the same class, but they respond to HTTPManager's messages differently.
HTTPManager expects a delegate to implement the HTTPManagerDelegate protocol and both LoginManager and FetchManager do this. The Login- and FetchManager classes also provide a protocol for my application delegate so that the data can make its way all the way back to the user interface.
Within my application delegate's init: method, I initialize both a login and a fetch manager and get the following warnings for both:
warning: class 'MyAppDelegate' does not implement the 'HTTPManagerDelegate' protocol
warning: incompatible Objective-C types assigning 'struct HTTPManager *', expected 'struct LoginManager *'
Neither of the two classes being initialized are derived from HTTPManager, but they do implement the HTTPManagerDelegate protocol. The line of code that produces the above warning is:
_loginMgr = [[LoginManager alloc] initWithDelegate:self];
So what on earth is making LoginManager's initWithDelegate: method return an HTTPManager*? There is no inheritance and my return types are correct, so to me this is some dark form voodoo that I cannot best.
Here is the shell of my application. There are probably typos and small inconsistencies so ask me before assuming a syntactical problem:
// HTTPManager.h
#protocol HTTPManagerDelegate
...
#end
#interface HTTPManager : NSObject
{
id <HTTPManagerDelegate> _delegate;
...
}
- (HTTPManager *) initWithDelegate:(id <HTTPManagerDelegate>)delegate;
...
#end
// LoginManager.h
#protocol LoginManagerDelegate
...
#end
#interface LoginManager : NSObject <HTTPManagerDelegate>
{
id <LoginManagerDelegate> _delegate;
...
}
- (LoginManager *) initWithDelegate:(id <LoginManagerDelegate>)delegate;
...
#end
// MyAppDelegate.h
#interface MyAppDelegate : NSObject <NSApplicationDelegate, LoginManagerDelegate, FetchManagerDelegate>
{
LoginManager *_loginMgr;
...
}
...
#end
// MyAppDelegate.m
...
- (MyAppDelegate *) init
{
self = [super init];
if (self)
{
// WARNING HAPPENS HERE
_loginMgr = [[LoginManager alloc] initWithDelegate:self];
...
}
return self;
}
...
Thanks in advance.
The problem is that you have two methods with the same method signature -initWithDelegate: but with different types in their arguments and/or return types. The compiler cannot handle this case very well and in certain cases, it could also lead to errors at runtime (not in your case because the types in your methods do not differ in size, they're all pointers).
The reason for this (AFAIK) is that the runtime has no straightforward access to the types used in a method. It just reads a selector (which contains no type information) and decides based on this selector what method to call. To help the runtime pack the method arguments onto the stack, the compiler creates a table at compile time that maps selectors to the argument and return value types. This table has just one entry per selector. So if two methods exist that have the same selector but different types in arguments or return value, this system can fail.
In your case:
-init... methods should always return id and not a specific type.
This solves the problem of different return types. The other problem (different argument types) is harder to solve. You can either omit the protocol specification from your method declaration (initWithDelegate:(id)delegate) or give the two methods different names:
- (id) initWithHttpMgrDelegate:(id <HTTPManagerDelegate>)delegate;
- (id) initWithLoginMgrDelegate:(id <LoginManagerDelegate>)delegate;