替换目标C中的类方法并调用原始实现

时间:2015-03-10 09:11:23

标签: objective-c introspection objective-c-runtime

我正在尝试用自己的实现替换UIImage的类方法。

在某些情况下,我的实现可能只想调用原始的UIImage实现。

这是我的代码

#import "UIImage+SkinnedImage.h"
#import <objc/runtime.h>

@interface UIImage (SkinnedImagePrivate)

+ (UIImage *)originalImageNamed:(NSString*)name;

@end


@implementation UIImage (SkinnedImage)

+ (void)allowSkinning {
    Method imageNamedMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
    IMP originalImageNamedIMP = class_getMethodImplementation_stret([UIImage class], @selector(imageNamed:));
    Method swizzled = class_getClassMethod([UIImage class], @selector(skinnedImageNamed:));
    method_exchangeImplementations(imageNamedMethod, swizzled);
    const char *signatureEnconding = method_getTypeEncoding(imageNamedMethod);
    class_addMethod([UIImage class], @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);

}


+ (UIImage *)skinnedImageNamed:(NSString *)name {
    //XXX 
    return [UIImage originalImageNamed:name];
}

@end
  1. + (void)allowSkinning被召唤。
  2. + (UIImage *)skinnedImageNamed:(NSString *)name被调用而不是+ (UIImage *)imageNamed:(NSString *)name;
  3. return [UIImage originalImageNamed:name];因错误而崩溃:
  4.   

    原因:'+ [UIImage originalImageNamed:]:无法识别的选择器发送到   类

    为什么要class_addMethod([UIImage类],@ selector(originalImageNamed :),originalImageNamedIMP,signatureEnconding);未能为我提供实施?

    请注意,函数class_addMethod返回true

1 个答案:

答案 0 :(得分:3)

此行将实例方法-originalImageNamed:添加到UIImage

class_addMethod([UIImage class], @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);

您可以将其更改为:

class_addMethod(object_getClass([UIImage class]), @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);

或等同地:

class_addMethod(objc_getMetaClass("UIImage"), @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);

类方法是类的元类的实例方法。因此,向该元类添加实例方法会向类添加类方法。

您拨打class_getMethodImplementation_stret()的问题也存在类似问题。该函数获取实例方法的实现。因此,如果你想获得一个类方法,你必须通过该类的元类:

IMP originalImageNamedIMP = class_getMethodImplementation(object_getClass([UIImage class]), @selector(imageNamed:));

或者,既然您已经拥有相应的Method,那么您可以这样做:

IMP originalImageNamedIMP = method_getImplementation(imageNamedMethod);

顺便问一下,你为什么要使用class_getMethodImplementation_stret()+imageNamed:不会返回结构。