我的工程结构,如图 1-0

         图  1-0

 

 在看具体实现以前,先捋以下 实现思路。

 ViewController 中有一个-(void)Amethod;A方法。

-(void)Amethod{
    NSLog(@"Amethod");
}

 1.swizzle method

在ViewController 的 -(void)viewDidLoad中调用 Amentohd;运行输出

                                         图 1-1

创建一个ViewController 的Category,重写+(void)Load;方法(因为这个方法只会在类被加载时,调用一次。至于initialize,它们之间区别,参考 iOS – + initialize 与 +load),在实现-(void)Bmethod;

+(void)load{
     Class class = [self class];
    Method m3 =class_getInstanceMethod(class, @selector(Amethod));
    Method m4  =class_getInstanceMethod(class, @selector(Bmethod));
    method_exchangeImplementations(m3, m4);
    
}

-(void)Bmethod{

    NSLog(@”swizzle method succuess :Bmenthod”);

}

运行结果如下图1-2,

                        1-2

这就是一个简单的交换方法的思路,如果你认真的看了上面的 参考文章,就会发现+(void)Load;是在所有类加载的时候执行的, 那么如果在这个方法里,执行过多操作,是会推延APP启动时间的。所以不要在里面做过多的操作。

由于swizzle method is not atomic,不是线程安全的,所以你可以在dispatce_once 中完成swizzle。

+(void)load{
            Class class = [self class];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method m3 =class_getInstanceMethod(class, @selector(Amethod));
        Method m4  =class_getInstanceMethod(class, @selector(Bmethod));
/*下面代码的主要思路就是 主类有没有实现Amethod, 实现了,就交换Amenthod和Bmenthod,没实现就给她加上Amethod,但IMP和enconding type是Bmethod的。
在加上Bmenthod,但IMP 和 encoding type是Amethod的。
*/ 

 

        BOOL success = class_addMethod(class, method_getName(m3), method_getImplementation(m4), method_getTypeEncoding(m4));
        if (success) {
            class_replaceMethod(class, method_getName(m4), method_getImplementation(m3), method_getTypeEncoding(m3));
        }else{
        method_exchangeImplementations(m3, m4);
        }
    });
}

 以上,就是 简单的swizzle method。你也可以在Bmethod 中再次调用Bmethod。不会出现递归。动动脑子就知道了。

2.消息转发 : 有三种方法是去补救实现相应方法。也可以做多继承使用。

 我们在ViewController 中声明一个 button ,但是不实现它的绑定方法

    UIButton *pushA = [[UIButton alloc]initWithFrame:CGRectMake(200, 200, 50, 50)];
    [pushA setTitle:@"pusha" forState:(UIControlStateNormal)];
    [pushA addTarget:self action:@selector(pushA:) forControlEvents:(UIControlEventTouchUpInside)];
    [self.view addSubview:pushA];

在它的分类中,实现下面代码,当一个实例方法没有实现就会调用 +(BOOL)resolveInstanceMethod:(SEL)sel;相对应对还有类方法+(BOOL)resolveClassMethod:(SEL)sel;

+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selectionString = NSStringFromSelector(sel);
    if ([selectionString isEqualToString:@"pushA:"]) {
        class_addMethod(self, sel, (IMP)pushA, "v@:*");// 在我之前关于YYModel的文中提到 Encoding type
    }
    return [super resolveInstanceMethod:sel];// 这里无论返回yes 还是NO ,如果方法没有实现都会调用第二种补救方式
}
//函数的具体实现 也是IMP
void pushA(id self,SEL sel,UIButton *sender){ NSLog(@"动态添加方法%@",self); AViewController *vc = [AViewController new]; AViewController *sourceVC = self; [sourceVC.navigationController pushViewController:vc animated:YES]; }

  上面是第一种 补救方法。第二种就是-(id)forwardingTargetForSelector:(SEL)aSelector;

在ViewController 中在[self performSelector:@selector(Cmethod)];但不在Viewcontroller 中实现Cmenthod(关于 -(void)performSelector 还有很多方法,也会稍后添加)

在分类中,添加转发方法,

-(id)forwardingTargetForSelector:(SEL)aSelector{
    AViewController * avc = [AViewController new];
    if ([avc respondsToSelector:aSelector]) {
         return avc;
    }
    return [super forwardingTargetForSelector:aSelector];
}

在AViewController实现 -(void)Cmenthod;就完成消息转发给另一个对象。

第三种补救方式是- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;配合- (void)forwardInvocation:(NSInvocation *)anInvocation

使用,其中还要介绍NSInvocation 对象。还有 – (void)doesNotRecognizeSelector:(SEL)aSelector; 所以今天先到这,以后会补充

 

版权声明:本文为DafaRan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:http://www.cnblogs.com/DafaRan/p/7878226.html