method swizzling

替换方法的作用一般是在原方法的基础上加上我们想要加的功能,就是改变其IMP,所以我们调用的时候还是调用原方法,不需要去修改

一. 在分类中重写之前的方法(即覆盖了原方法)

在分类中重写之前的方法:
People.h

@interface People : NSObject
- (void)say ;
@end

People +Swizzling.m

#import "People +Swizzling.h"

@implementation People (Swizzling)
- (void)say {
    NSLog(@"People +Swizzling.h  --- say");
}
@end

但是这样做的缺点有2点:
1.在分类中重写方法会覆盖掉原方法的功能(即调用不了原方法),
2,如果多个分类都重写了一个方法,运行时机制无法确定会一直调用哪个方法;

二. 使用运行时的交换方法(method swizzling)

写一个不同名的方法, 使用运行时将该方法与原方法交换, (子类的方法列表并不包含父类中的方法)

  • 分2种情况:
  • 原方法为父类方法,子类没有
  • 方法和替换方法都实现于子类
Method m1 = class_getInstanceMethod(self, @selector(eat));
   Method m2 = class_getInstanceMethod(self, @selector(son_eat));

   BOOL add = class_addMethod(self, @selector(eat), method_getImplementation(m2), method_getTypeEncoding(m2));

1.第一种情况:子类没有实现替换方法时,需要检测(将原方法名SEL和替换方法的实现IMP,加入方法到本类中), 如果能加入,这时原方法的方法实现指针指向了替换方法, 再将替换方法SEL的IMP指向原方法的IMP就完成了方法交换;

if (add) {
        class_replaceMethod(self, @selector(son_eat), method_getImplementation(m1), method_getTypeEncoding(m1));
    }

2.第二种情况:俩个方法都在子类中已经实现,就直接交换方法即可

else {
       method_exchangeImplementations(m1, m2);

   }
- (void)eat {
    NSLog(@"%s", __FUNCTION__);
}

- (void)son_eat {
    
    [self son_eat];
    
    NSLog(@"%s", __FUNCTION__);
}

确保该代码只会调用一次,避免多次调用替换混乱
一般是在+ (void)load 方法中

方法交换

三.C指针

  • (方法替换的本质:将原方法的方法实现指向一个新的方法,新方法里面需要调用原方法的实现,即新方法是私有的;)
void (*gOrigDrawRect)(id, SEL, NSRect);
+ (void)load
{
        Method origMethod = class_getInstanceMethod(self, @selector(drawRect:));
        gOrigDrawRect = (void *)method_getImplementation(origMethod);
 if(!class_addMethod(self, @selector(drawRect:), (IMP)OverrideDrawRect, method_getTypeEncoding(origMethod)))
            method_setImplementation(origMethod, (IMP)OverrideDrawRect);
}

static void OverrideDrawRect(NSView *self, SEL _cmd, NSRect r)
{
        gOrigDrawRect(self, _cmd, r);
        [[NSColor blueColor] set];
        NSRectFill(r);
}
参考
  • Method Replacement for Fun and Profit

你可能感兴趣的:(method swizzling)