方法混合(Method Swizzling):动态替换已有类的方法

   动态替换类的方法在不能改变原有代码的情况下,提供了一种解决方法。貌似这种方法不太常用,不过记下来以备后患~。

  功能:用这种方法可以给你的程序提供一些一般方法不能实现的功能。

  例子:UIWebView的delegate接口提供了一个shouldStartLoadWithRequest()方法,可以通过NSURLRequest这个对象得到所要装载网页 的详情(head头信息等),但你不能修改这个对象(添加或修改你需要的HTTP请求头信息,比如:“User-Agent”字段),如果HTTP请求的对端,(比如硬件设备)需要各种特殊的头信息,将束手无策。

但如果使用方法混合(Method Swizzling)计数就可以实现动态修改接受类的方法。

  注意:技术比较强大,但比较危险(具体还未知。。。待确定)

  实现:(以代码为例子)将SimpleClass类的print方法动态替换成Swizzle类的swizzlePrint方法:

 

//Swizzle.h
#import <Foundation/Foundation.h>
@interface Swizzle : NSObject {
}
+ (BOOL)swizzle:(Class)c1 oSelector:(SEL)orig Class:(Class)c2 aSelector:(SEL)newSel;
- (void)testSwizzle;
@end

//Swizzle.m
#import "Swizzle.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface SimpleClass : NSObject {
}
- (void)print:(NSString *)str;
@end

@implementation SimpleClass
/*the old method to replace*/
- (void)print:(NSString *)str {
    NSLog(@"Input String is : %@", str);
}
@end

@implementation Swizzle

/*****************************************************************************
 功能描述  : 将类c1中方法orig的实现替换为类c2中方法newSel的实现
 输入参数  : (Class)c1
             (SEL)orig
             (Class)c2
             (SEL)newSel
 返 回 值  : BOOL
*****************************************************************************/
+ (BOOL)swizzle:(Class)c1 oSelector:(SEL)orig Class:(Class)c2 aSelector:(SEL)newSel{
    /*注意区分实例方法和类方法:实例方法用getInstanceMethod,类方法:getClassMethod*/
    Method origMethod = class_getInstanceMethod(c1, orig);
    Method newMethod = class_getInstanceMethod(c2, newSel);
    
    if (NULL == origMethod)
    {
        NSLog(@"can't find orig method %@ in class %@",NSStringFromSelector(origMethod),c1);
        return NO;
    }
    if (NULL == newMethod)
    {
        NSLog(@"can't find new method %@ in class %@",NSStringFromSelector(newSel),c2);
        return NO;
    }
    
    /*class_addMethod:add a new method to a class with a given name and implementation(详见官方文档)*/
    BOOL added = class_addMethod(c1, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    
    if(added)
    {
        /*if added success just replace :NO TEST---MAY HAVE SOME PROBLEM*/
        class_replaceMethod(c1, newSel, method_getImplementation(origMethod)), method_getTypeEncoding(origMethod));
    }else
    {
        /*change two method's implementation*/
        method_exchangeImplementations(origMethod, newMethod);
    }
    return YES;
}
/*The new method*/
- (void)swizzlePrint:(NSString *)str {
    NSLog(@"After Swizzle:%@",str);
}
/*a test method*/
- (void)testSwizzle {
    SEL oriSel = @selector(print:);
    SEL newSel = @selector(swizzlePrint:);
    
    SimpleClass *simple = [[SimpleClass alloc] init]];
    [simple print:@"Hello World"];
    
    [Swizzle swizzle:[simple class] oSelector:oriSel Class:[self class] aSelector:newSel];
    [simple print:@"Hello World"];
    
    [simple release];
}

 

 

posted on 2013-08-05 18:39  kelisi_king  阅读(922)  评论(0编辑  收藏  举报