【问题标题】:Injecting and then using a method注入然后使用方法
【发布时间】:2020-10-28 09:19:35
【问题描述】:

我在玩方法注入,但在这个上烧了我的手指。

有两个班级,猫和狗。狗试图通过调动speak 方法来找出猫的秘密。狗确实发现猫有一个catSecret 方法,但无法动态注入替代方法。为什么?

Dog端的相关方法是

- ( NSString * ) speak
{
    return @"Woof";
}

- ( NSString * ) wannabeCat
{
    return @"Meow?";
}

+ ( BOOL ) resolveInstanceMethod:( SEL ) aSelector
{
    NSString * s = NSStringFromSelector ( aSelector );

    if ( [s containsString:@"Secret"] )
    {
        NSLog ( @"Oops - the cats have a %@", s );
        
        IMP m = [Dog.class methodForSelector:@selector ( wannabeCat )];
        
        class_addMethod ( Dog.class, aSelector, m, "@@:" );
        
        NSLog ( @"\tAdded" );
        
        return YES;
    }
    else
    {
        NSLog ( @"Skipping %@", s );
        return [super resolveInstanceMethod:aSelector];
    }
}

编辑

之前我尝试在 forwardingTargetForSelector 方法中做同样的事情,但在来自 Cy-4AH 的 cmets 之后,我将其移至 resolveInstanceMethod,这似乎是正确的地方。

狗希望在此之后,Dog 类有一个添加的方法,但这不起作用。它仍然以'-[Dog catSecret]: unrecognized selector sent to instance 消息终止。我的问题是 - 为什么这不起作用?

Cat 方面,这是相关的部分。

- ( NSString * ) catSecret
{
    return @"scratch";
}

- ( NSString * ) speak
{
        return [NSString stringWithFormat:@"Miaau (%@)", self.catSecret] 
}

我希望在 Dog 端动态注入 catSecret 选择器和 wannabeCat 实现。

输出如下所示

2020-10-28 12:05:12.613324+0200 Swizzle[4786:119045] Oops - the cats have a catSecret
2020-10-28 12:05:12.613357+0200 Swizzle[4786:119045]    Added
2020-10-28 12:05:12.613434+0200 Swizzle[4786:119045] Skipping _forwardStackInvocation:
2020-10-28 12:05:12.613482+0200 Swizzle[4786:119045] -[Dog catSecret]: unrecognized selector sent to instance 0x1006b2730

【问题讨论】:

  • forwardingTargetForSelector是selector的转发目标,意思是找一个能做selector的人。要为选择器动态添加实现,您需要 resolveInstanceMethod:
  • 是的 - 我希望在forwardingTargetForSelector 中注入该方法,这就是为什么我从那里返回self ...但我会尝试在resolveInstanceMethod: 中进行...跨度>
  • 试试把你的测试代码放在dyspatch_asynch里面,可能你要测试的时间太早了
  • 我将代码移至+resolveInstanceMethod:,然后添加它没问题。我认为这是这样做的方法,谢谢,但它仍然以无法识别的选择器异常告终。我记录了它想要解析的选择器,并在+resolveInstanceMethod 中获得了一个_forwardStackInvocation:,我不知道如何处理。我想我只是在寻找一种动态注入然后使用方法的方法,而我尝试的方法不起作用。我见过的例子通常注入一些已知的方法,但在这里。方法是动态发现的。

标签: objective-c objective-c-runtime


【解决方案1】:

编辑

基于 Cy-4AH cmets 进行清理。

第一步,更大的一步是将方法注入移动到resolveInstanceMethod,我已经编辑了问题以反映这一点。这也是由于 Cy-4AH,现在我几乎可以毫无错误地打字了。

然而,最终的修复是一个小问题......代码在问题中的方式

IMP m = [Dog.class methodForSelector:@selector ( wannabeCat )];

指的是类方法,而不是实例方法。这必须改为

IMP m = class_getMethodImplementation ( Dog.class, @selector ( wannabeCat ) );

让它成为一个实例方法,然后它就可以正常工作了,让狗高兴...

供参考,完整的新方法变为

+ ( BOOL ) resolveInstanceMethod:( SEL ) aSelector
{
    if ( [super resolveInstanceMethod:aSelector] )
    {
        return YES;
    }
    else
    {
        NSLog ( @"Oops - the cats have a %@", NSStringFromSelector( aSelector ) );
        
        IMP m = class_getMethodImplementation ( Dog.class, @selector ( wannabeCat ) );
        
        class_addMethod ( Dog.class, aSelector, m, "@@:" );
        
        NSLog ( @"\tAdded" );
        
        return YES;
    }
}

【讨论】:

  • 你需要class_getMethodImplementation(self, @selector ( wannabeCat )) 而不是[[Dog alloc] init...
  • 是的!更干净,谢谢...我已将其编辑为答案。
猜你喜欢
  • 2022-01-15
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多