Cascading delegates and "Code That Doesn't Do What It Says"
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 pr开发者_如何转开发otocol 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;
精彩评论