替换 objective C 中的 class 方法并调用原始实现

Replacing a class method in objective C and calling original implementation

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

在某些情况下,我的实现可能只想调用原始的 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]; 崩溃并出现错误:

reason: '+[UIImage originalImageNamed:]: unrecognized selector sent to class

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

请注意函数 class_addMethod returns true

此行将实例方法 -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 方法是 class 的元 class 上的实例方法。因此,向该元 class 添加实例方法会向 class.

添加 class 方法

您对 class_getMethodImplementation_stret() 的调用也存在类似问题。该函数获取实例方法的实现。因此,如果你想得到一个class方法,你必须通过class的meta-class:

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

或者,因为您已经有了相应的 Method,您可以这样做:

IMP originalImageNamedIMP = method_getImplementation(imageNamedMethod);

顺便问一下,你为什么要使用 class_getMethodImplementation_stret()+imageNamed: 没有 return 结构。