在没有子类化的情况下向方法添加其他行为

时间:2013-05-10 14:38:56

标签: ios objective-c oop objective-c-blocks

我需要为需要扩展的方法添加其他行为,即实现类似于

的方法
- (void)extendMethod:(SEL)selector forClass:(Class)class withCompletionBlock:(void (^)(void))completionBlock;

因此,每次Class实例调用带有SEL选择器的方法时,都应调用我的完成块

我尝试过方法调整,但遇到了一些问题:我希望调用原始方法实现。

我需要的是与子类化非常相似,但是应该实现而不进行子类化

更新

例如,我有UIViewController的子类,名为MyViewControllerMyViewController- (void)viewDidLoad方法。在应用程序的某处我调用方法

[methodExtender extendMethod:@selector(viewDidLoad)
                    forClass:[MyViewController class]
         withCompletionBlock:^{
             NSLog(@"view did load called");
         }];

所以在MyViewController的每个实例的viewDidLoad方法之后调用我的完成块。

4 个答案:

答案 0 :(得分:1)

我不确定你想如何使用选择器,但是你可以尝试使用Objective-C中的机制来扩展任何类(甚至是那些你没有实现文件的类)。

  1. 从Xcode点击File-> New-> File(command + n)
  2. 来自Cocoa Touch选择Objective-C类别
  3. 输入您的类别的名称,然后选择要进行分类的类(我选择了UIButton)
  4. 然后是下一步,创建。
  5. Xcode将创建两个文件,例如:UIButton+extendMethod.h 在头文件中声明您的方法并在* .m文件中实现它。

    如果您想使用,请在* .h文件导入中说明您的View Controller #import "UIButton+extendMethod.h"

    然后你可以像这样调用你的方法:

    UIButton *button = [[UIButton alloc] init]; [button extendMethod:@selector(yourMethod:)];

答案 1 :(得分:0)

Swizzling确实允许你调用原始实现,虽然它有点令人困惑。因为实现是在调配后交换的,所以使用swizzled方法的选择器调用原始实现:

- (void)mySwizzledImplementation {
    // do stuff
    // now call original implementation (using swizzled selector!)
    [self mySwizzledImplementation];
    // do more stuff
}

另见http://cocoadev.com/wiki/MethodSwizzling

答案 2 :(得分:0)

在没有子类化的情况下,没有办法(不再)模拟继承。有使用Posing,方法调整就是剩下的东西(虽然不像摆姿势那样优雅)。这是在能够调用原始实现时正确调整方法的一种方法。

int swizzle_instance_methods(Class class, SEL selector, IMP replacement, IMP *store) {

    @synchronized(class) {
        Method method = class_getInstanceMethod(class, selector);
        IMP original_imp = NULL;

        if (method != NULL) {
            const char *type = method_getTypeEncoding(method);
            IMP original_imp = class_replaceMethod(class, selector, replacement, type);
            if (original_imp == NULL)
                original_imp = method_getImplementation(method);

            if (original_imp != NULL && store != NULL) {
                *store = original_imp;
            }
        }
        return (original_imp != NULL);
    }
}

+ (void) load
{
    static IMP originalMethodImpl = NULL;
    IMP customMethodImpl = imp_implementationWithBlock(^(id self_) { 
        NSString *descr = ((NSString(*)(id,SEL))originalMethodImpl)(self_, @selector(description); 
        return [NSString stringWithFormat:@"<--- %@ --->",descr];
    }); 

    swizzle_instance_methods([self class], @selector(description), customMethodImpl, &originalMethodImpl);
}

我可能会补充一点,这对于调试很有用,我认为它可以用于构建优秀的框架。唉,Apple似乎有不同的想法,使用方法调配会导致您的应用程序被排除在App Store之外。如果您不是针对应用程序商店,那么所有权力都可以为您服务。

答案 3 :(得分:0)

可以使用ObjC类别。例如,您可以扩展NSString的hasPrefix方法,如下所示,

-(BOOL)hasPrefixx:(NSString *)aString
{
    NSLog(@"Checking has prefix");
    return [self hasPrefix:aString];

}

如果您导入类别,则应该可以调用此方法。但他的意思是你改变了你的电话中的方法。

顺便说一句,Method swizzling应该可行。一点点解释here