【问题标题】:When using Objection, implementation of custom protocol crashes with unrecognized selector使用 Objection 时,自定义协议的实现会因无法识别的选择器而崩溃
【发布时间】:2011-07-27 02:01:14
【问题描述】:

我正在定义一个自定义协议:

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

我想拥有不同的提供商。所以一个是 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

我将Objection 设置为从我的班级绑定到协议。

@interface NGSObjectionModule : ObjectionModule
@end

@implementation NGSObjectionModule

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

我设置了全局注入器:

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

  [Objection setGlobalInjector:injector];
}

我在我的 RootViewController 中这样使用它:

@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

当我运行它时,我收到以下错误:

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

所以我的班级实现了协议。它已成功分配给id&lt;NGSAuthProvider&gt;。我尝试显式构建[[NGSFacebookAuthProvider alloc] init] 而不是使用Objection,但它仍然崩溃。

我尝试使用objc/runtime.h 方法循环遍历选择器以查看存在哪些选择器,但它找到的唯一内容是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);
}

这一定是我想念的简单的东西。我使用 Cocoa 定义的协议,没有这个问题。我已经为基于UIViewController 的委托定义了自定义协议,没有问题。

我很困惑为什么 Obj-C 运行时找不到我的方法!如果我将 id&lt;NGSAuthProvider&gt; 更改为 NGSFacebookAuthProvider 并显式构建它,那么一切正常。

解决方案:

问题是我误解了如何绑定到协议。一种可行的方法是:

@implementation NGSObjectionModule

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

我想做的是将类绑定到协议,但 Objection 可能不知道要调用的初始化程序?

【问题讨论】:

标签: objective-c objective-c-runtime


【解决方案1】:

问题是您尝试使用静态类方法(表示因为您有一个 +)而不是在您的对象实例上运行的方法(这是您编写的,带有-)

【讨论】:

  • 我没有看到我在哪里使用类方法。
  • 我也是。但不知何故,你是,对吧?也许你认为你有一个对象的实例,但它是一个指向类型本身的指针(我假设这就是类方法的工作原理)
  • 你对调用静态类方法的评论让我思考。问题是我如何配置异议。我以为我正在将其设置为在请求协议时使用类对象来实例化实例。我必须在配置时实际创建一个实例。我将更新问题以包含解决方案。现在我在调试输出中看到它找不到+[NGSFacebookAuthProvider accessToken]
【解决方案2】:

克里斯,

您可以使用 Objection 的元类绑定,它允许您将元类绑定到协议并针对元类实例调用类方法。

例如,

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

但前提是你想使用类方法。否则,您可以对共享实例使用协议绑定。

【讨论】:

  • 我不太清楚为什么要将协议绑定到类对象。那么类对象是否必须实现协议?
  • 协议反映了元类的接口(实际上不一定,但如果你不这样做,你会得到编译器警告)。该协议只是充当一种绑定机制,以便 Objection 知道要将哪个对象绑定到属性。由于没有办法定义反映元类接口的属性,因此使用协议来做到这一点。请参阅github.com/atomicobject/objection 并阅读元类绑定部分。
  • 好吧,这不是我想要的(即类方法)。我有几个不同的NGSAuthProvider 协议实现,我想配置 Objection 以在请求协议时构造特定类的实例。所以我想让[self bind:[NGSFacebookAuthProvider class] toProtocol:@protocol(NGSAuthProvider)] 让Objection 创建NGSFacebookAuthProvider 的实例,但它却提供了类对象。
  • 啊,是的。在这种情况下,您可以使用共享实例 [self bind:[NGSFacebookAuthProvider sharedProvider] toProtocol:@protocol(NSGAuthProvider)] 或使用提供者机制来构建实例并返回它:[self bindProvider:[[[CarProvider alloc] init] autorelease] toClass:[Car class]][self bindBlock:^(JSObjectionInjector *context) { // Manually build object return car; } toClass:[Car class]]; }
  • 如果我的班级有objection_register_singleton(Class),那么我不必实现sharedProvider 班级属性,对吧?
【解决方案3】:

我也遇到了同样的问题

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

正确的工作方式是

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-08
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    • 2012-09-20
    • 2019-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多