OC 反射实现(runtime)集合Admob案例

前言

因需要做一个广告的聚合包,聚合包里包含了各个广告平台的SDK,当需要时,把SDK引入,不需要时把SDK删除即可,不需要动逻辑代码。比如:当需要接入admob时,只需要把admob的库引入项目中即可,不需要另外写代码,当不用admob时,把admob的库删除,此时,也不用修改代码。
因此,实现中,就不能直接用admob的对象,需要通过反射去实现。
Java可以用反射做,object-c、swift可以用runtime机制去做。以下用object-c实现一种方式,供参考。

步骤

  1. 提供一个方法,检测当前类是否实现delegate,如果没实现,则注册一个
//RunTimeTools.m
+ (Protocol *)objc_allocateProtocol:(const char *)name className:(Class) cls {
    // 检查该协议是否已经注册
    if (![self class_conformsToProtocol:cls protocol:NSProtocolFromString([NSString stringWithUTF8String:name])]) {
        Protocol *protocol = objc_allocateProtocol(name);
        if (protocol == nil) {
            protocol = objc_getProtocol(name);//NSProtocolFromString([NSString stringWithUTF8String:name]);
        }
        return protocol;
    }
    return nil;
}
//判断是否实现delegate
+ (BOOL)class_conformsToProtocol:(Class)class protocol:(Protocol *)protocol {
    if ([class conformsToProtocol:(protocol)]) {
        return YES;
    }else{ 
        return NO;
    } 
}
  1. 定义一个delegate的回调,如admob的请求广告事件(interstitialDidReceiveAd:)
typedef void (^InterstitialDidReceiveAdBlock)(__weak id slf, id ad);
  1. 在load方法里,获取delegate,并替换方法
//GADInterstitialDelegate是admob的delegate
Protocol *protocol = [RunTimeTools objc_allocateProtocol:"GADInterstitialDelegate"
                          className:[AdMobInterstitialAdTask class]];
//interstitialDidReceiveAd 是admob收到广告的回调
SEL selector = @selector(interstitialDidReceiveAd:);
SEL swizzledSelector = [FTAPRunTimeTools swizzledSelectorForSelector:selector];
struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);

 InterstitialDidReceiveAdBlock undefinedBlockReceive2 =  ^(id slf, id ad) {
        NSLog(@"admob 请求成功1  %@ ", [ad valueForKey:@"adUnitID"]);
    };
 InterstitialDidReceiveAdBlock implementationBlockReceive2 =  ^(id slf, id ad) {
        NSLog(@"%@ admob 请求成功2 %@ ",FTAPLogHead , [ad valueForKey:@"adUnitID"]);
    };
//动态注册
    [RunTimeTools replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:clz withMethodDescription:methodDescription implementationBlock:implementationBlockReceive2 undefinedBlock:undefinedBlockReceive2];
  1. 动态注册方法
+ (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock {
    if ([self instanceRespondsButDoesNotImplementSelector:selector class:cls]) {
        NSLog(@" 替换方法失败 ");
        return;
    }
    if ([cls instancesRespondToSelector:selector]) {
        NSLog(@" 已经有方法实现了,使用implementationBlock");
        return;
    }
    IMP implementation = imp_implementationWithBlock((id)([cls instancesRespondToSelector:selector] ? implementationBlock : undefinedBlock));
    
    Method oldMethod = class_getInstanceMethod(cls, selector);
    if (oldMethod) {
        class_addMethod(cls, swizzledSelector, implementation, methodDescription.types);
        
        Method newMethod = class_getInstanceMethod(cls, swizzledSelector);
        
        method_exchangeImplementations(oldMethod, newMethod); 
    } else {
        class_addMethod(cls, selector, implementation, methodDescription.types);
    }
}

+ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls {
    if ([cls instancesRespondToSelector:selector]) {
        unsigned int numMethods = 0;
        Method *methods = class_copyMethodList(cls, &numMethods);
        
        BOOL implementsSelector = NO;
        for (int index = 0; index < numMethods; index++) {
            SEL methodSelector = method_getName(methods[index]);
            if (selector == methodSelector) {
                implementsSelector = YES;
                break;
            }
        } 
        free(methods); 
        if (!implementsSelector) {
            return YES;
        }
    } 
    return NO;
}

核心原理,就是检测下是否存在方法的实现,如果没有就class_addMethod添加一个方法,如果存在就method_exchangeImplementations替换下方法实现。

  1. 获取admob的对象
@property (nonatomic, strong) id interstitial;
Class adMobileAds = NSClassFromString(@"GADMobileAds");
self.interstitial = [admoInterstitial alloc]; 
  1. 设置广告id
if([self.interstitial respondsToSelector:@selector(initWithAdUnitID:)]){
        [self.interstitial performSelector:@selector(initWithAdUnitID:) withObject:self.getAdID];
  }
  1. 设置admob的delegate
[self.interstitial setValue:self forKey:@"delegate"];
  1. 获取admob的请求对象
Class admobRequest = NSClassFromString(@"GADRequest");
    id request;
    if ([admobRequest respondsToSelector:@selector(request)]) {
         request = [admobRequest performSelector:@selector(request)];
    }
  1. 请求广告
 if ([self.interstitial respondsToSelector:@selector(loadRequest:)]) {
        [self.interstitial performSelector:@selector(loadRequest:) withObject: request];
    } 

至此,利用runtime机制,实现了不明文引用admob的对象,进行请求广告。

利用此方法接入三方库,要特别注意三方库的版本,避免版本不一样,导致方法的变动。

另外,提供一个代码设置admob的测试账号方法:

//1. 获取admobDeviceID
- (NSString *) admobDeviceID
{
    NSUUID* adid = [[ASIdentifierManager sharedManager] advertisingIdentifier];
    const char *cStr = [adid.UUIDString UTF8String];
    unsigned char digest[16];
    CC_MD5( cStr, strlen(cStr), digest );
    
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    
    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++){
        [output appendFormat:@"%02x", digest[i]]; 
        }
    return  output; 
}
//2. 把当前设备标记为测试设备
   [request setValue:@[[self admobDeviceID]] forKey:@"testDevices"];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值