2011-07-27 7 views
5

estoy definiendo un protocolo personalizado:Al utilizar las objeciones, la implementación del protocolo personalizado se estrella con el selector no reconocido

@protocol NGSAuthProvider <NSObject> 
- (BOOL)isReady; 
- (BOOL)isSessionValid; 
- (void)login; 
- (void)logout; 
- (NSString *)accessToken; 
- (BOOL)handleOpenURL:(NSURL *)url; 
@end 

Quiero tener diferentes proveedores. Así que uno es un proveedor de Facebook:

@interface NGSFacebookAuthProvider : NSObject <NGSAuthProvider> 
@end 

@interface NGSFacebookAuthProvider() <FBSessionDelegate> 
@property BOOL ready; 
@property(nonatomic, retain) Facebook *facebook; 
@property(nonatomic, retain) NSArray *permissions; 
@end 

@implementation NGSFacebookAuthProvider 
//Implementation of fbLogin, fbLogout and the methods in NGSAuthProvider that forward calls to self.facebook 
- (NSString *)accessToken 
{ 
    return [self.facebook accessToken]; 
} 

@end 

configuración que Objection para unirse a mi clase con el protocolo.

@interface NGSObjectionModule : ObjectionModule 
@end 

@implementation NGSObjectionModule 

- (void)configure 
{ 
    self bind:[NGSFacebookAuthProvider class] toProtocol:@protocol(NGSAuthProvider)]; 
} 
@end 

puedo configurar el inyector global:

@implementation NGSAppDelegate 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    ObjectionModule *module = [[NGSObjectionModule alloc] init]; 
    ObjectionInjector *injector = [Objection createInjector:module]; 
    [module release]; 

    [Objection setGlobalInjector:injector]; 
} 

estoy usando esto en mi RootViewController así:

@interface RootViewController : UITableViewController 
@end 

@interface RootViewController() 
@property(nonatomic, retain) id<NGSAuthProvider> authProvider; 
@end 

@implementation RootViewController 
- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.authProvider = [[Objection globalInjector] getObject:@protocol(NGSAuthProvider)]; 
} 

- (void)processConfig { 
    NSString *token = [self.authProvider accessToken]; 
    // use the access token 
} 
@end 

Cuando ejecuto esto, me sale el siguiente error:

2011-07-26 21:46:10.544 ngs[6133:b603] +[NGSFacebookAuthProvider accessToken]: unrecognized selector sent to class 0x30c7c 
2011-07-26 21:46:10.546 ngs[6133:b603] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NGSFacebookAuthProvider accessToken]: unrecognized selector sent to class 0x30c7c' 
*** Call stack at first throw: 
(
    0 CoreFoundation      0x00e825a9 __exceptionPreprocess + 185 
    1 libobjc.A.dylib      0x00fd6313 objc_exception_throw + 44 
    2 CoreFoundation      0x00e8417b +[NSObject(NSObject) doesNotRecognizeSelector:] + 187 
    3 CoreFoundation      0x00df3966 ___forwarding___ + 966 
    4 CoreFoundation      0x00df3522 _CF_forwarding_prep_0 + 50 
    5 ngs         0x0000324b -[RootViewController processConfig] + 731 
    6 ngs         0x000041a2 __33-[RootViewController viewDidLoad]_block_invoke_0 + 50 

Así que mi clase im completa el protocolo. Se asigna correctamente al id<NGSAuthProvider>. Intenté construir [[NGSFacebookAuthProvider alloc] init] explícitamente en lugar de usar Objection y aún se bloqueó.

me trataron bucle a través de los selectores utilizando objc/runtime.h métodos para ver cuál selectores están ahí, pero lo único que encuentra es initialize:

- (void)logSelectors:(id)obj 
{ 
    int i=0; 
    unsigned int mc = 0; 
    Method * mlist = class_copyMethodList(object_getClass([obj class]), &mc); 
    NSLog(@"%d methods", mc); 
    for(i=0;i<mc;i++) 
     NSLog(@"Method no #%d: %s", i, sel_getName(method_getName(mlist[i]))); 

    free(mlist); 
} 

Esto tiene que ser algo simple que me falta. Uso protocolos definidos por Cocoa y no tengo este problema. He definido protocolos personalizados para delegados basados ​​en UIViewController sin problema.

¡No sé por qué Obj-C runtime no puede encontrar mis métodos! Si cambio id<NGSAuthProvider> a NGSFacebookAuthProvider y lo construyo explícitamente, entonces todo funciona.

SOLUCIÓN:

El problema era que no he entendido cómo enlazar a un protocolo. Una manera en que funciona es:

@implementation NGSObjectionModule 

- (void)configure 
{ 
    [self bind:[[[NGSFacebookAuthProvider alloc] init] autorelease] toProtocol:@protocol(NGSAuthProvider)]; 
} 
@end 

lo que me gustaría hacer es vincular una clase a un protocolo, pero las objeciones probablemente no sabrían el inicializador para llamar?

+0

Hola, me he referido [http://ravelantunes.com/blog/ inyección de dependencia madura /]. Pero [- (void) configure { [self bind: [MyUserService class] toProtocol: @protocol (UserService)]; }] tiene un error al definir clase. ¡Tu solución funcionó! –

Respuesta

0

El problema es que estás tratando de usar el método de clase estática (indicado porque tienes un +) en lugar de ejecutar un método en una instancia de tu objeto (que es como lo has escrito, con a -)

+0

No veo dónde estoy usando un método de clase. – chrish

+0

Yo tampoco. Pero de alguna manera lo estás, ¿verdad? Tal vez usted piensa que tiene una instancia de un objeto, sino que es un puntero a la clase en sí (que es como métodos de clase trabajan bajo el capó supongo) – Nektarios

+1

Usted comenta en llamar a un método de clase estática me hizo pensar. El problema es cómo estoy configurando Objeción. Pensé que estaba configurando para usar un objeto de clase para instanciar una instancia cuando se solicita un protocolo. Tengo que crear una instancia al configurarlo. Actualizaré la pregunta para incluir la solución. Ahora veo en la salida de depuración que no pudo encontrar '+ [NGSFacebookAuthProvider accessToken]'. – chrish

0

Chris,

usted podría utilizar los enlaces de meta clases de objeción de que le permite vincular una clase de meta con un protocolo e invocar métodos de clase contra la instancia de clase meta.

Por ejemplo,

[self bindMetaClass:[NGSFacebookAuthProvider class] toProtocol:@protocol(NGSAuthProvider)]; 

Pero sólo si desea utilizar métodos de clase. De lo contrario, puede usar los enlaces de protocolo en una instancia de recurso compartido.

+0

No estoy muy seguro de por qué vincularía un protocolo al objeto de clase. Entonces, ¿el objeto de la clase tiene que implementar el protocolo? – chrish

+0

El protocolo refleja la interfaz (no tiene que ser real, pero obtendrá advertencias del compilador si no) de la metaclase. El protocolo simplemente actúa como un mecanismo vinculante para que la Objeción sepa qué objeto unir a una propiedad. Como no hay una forma de definir una propiedad que refleje la interfaz de la metaclase, el protocolo se usa para hacer eso. Ver, https://github.com/atomicobject/objection y leer la sección de enlace de metaclase. – justice

+0

Ok, bueno eso no es lo que quería (es decir, métodos de clase). Tenía varias implementaciones diferentes del protocolo 'NGSAuthProvider' y quería configurar Objection para construir una instancia de una clase específica cuando se solicitaba un protocolo. Así que quería '[auto aprieto: [clase NGSFacebookAuthProvider] toProtocol: @protocol (NGSAuthProvider)]' para hacer que las objeciones para crear una instancia de 'NGSFacebookAuthProvider' pero en cambio siempre y cuando el objeto de la clase. – chrish

0

también se enfrentaron al mismo problema que yo estaba usando

[self bind:[MyClass class] toProtocol:@protocol(MyProtocol)]; 

La forma correcta para que funcione es

[self bindClass:[MyClass class] toProtocol:@protocol(MyProtocol)]; 
Cuestiones relacionadas