I am trying to replace a class method of UIImage with my own implementation.
In some cases, my implementation might just want to call the original UIImage implementation.
Here is my code
#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
+ (void)allowSkinning
is called.+ (UIImage *)skinnedImageNamed:(NSString *)name
is called instead of + (UIImage *)imageNamed:(NSString *)name;
return [UIImage originalImageNamed:name];
crash with error:reason: '+[UIImage originalImageNamed:]: unrecognized selector sent to class
Why did class_addMethod([UIImage class], @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding); failed to provide me with an implementation ?
Please note that the function class_addMethod returns true
This line adds an instance method, -originalImageNamed:
, to UIImage
:
class_addMethod([UIImage class], @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);
You can change it to:
class_addMethod(object_getClass([UIImage class]), @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);
Or, equivalently:
class_addMethod(objc_getMetaClass("UIImage"), @selector(originalImageNamed:), originalImageNamedIMP, signatureEnconding);
A class method is an instance method on the class's meta-class. So, adding an instance method to that meta-class adds a class method to the class.
There's a similar problem with your call to class_getMethodImplementation_stret()
. That function gets the implementation of an instance method. Therefore, if you want to get a class method, you have to pass the class's meta-class:
IMP originalImageNamedIMP = class_getMethodImplementation(object_getClass([UIImage class]), @selector(imageNamed:));
Or, since you already have the corresponding Method
, you could do:
IMP originalImageNamedIMP = method_getImplementation(imageNamedMethod);
By the way, why are you using class_getMethodImplementation_stret()
? +imageNamed:
does not return a structure.