美文网首页
基于协议分发器的 A/B Test 方案

基于协议分发器的 A/B Test 方案

作者: AppleTTT | 来源:发表于2017-10-26 14:35 被阅读110次

最近在看一些博客的时候,发现了一篇 iOS A/B Test 方案探索 ,突然想到在上家公司(做电商的)的时候,接到了产品提出的 A/B Test 需求的时候,非常的恐惧,当时并没有什么好方案,也是用最粗劣的方式实现的,看了这篇博客后,感觉当时能够想到或者看到这样的方案的话,就不会有这么多的苦恼了。

当时的苦恼

大多数情况下,A/B 方案基本上是 UI 全变,不同的用户看到的内容不尽相同,当时我们组的处理办法就是在各种代理的方法里面添加 if 或者是 switch 语句,这样导致代码结构很混乱,本来只有 300 多行代码的控制器,一下子变成了 600 行,后面改成用两个控制器做选择,但是这样修改的地方就很多了,而且在控制器外面判断也导致了诸多的问题,并非可行的方案,下面我们就说下 基于协议分发器的 A/B Test 方案 是怎么实现的吧。

可行发方案

  1. 抽离出 delegate 和 dataSource;即面对复杂的控制器,我们可以使用单独的类来实现这些代理方法。
  2. 协议分发;将协议与具体要实现的类结合起来,当需要此类去实现的时候,去指定具体的类去实现协议中的方法。
  3. 实现分发器的 “自释放” 。

具体的实现

抽离出 delegate 和 dataSource
@interface DelegateSource : NSObject <UITableViewDataSource, UITableViewDelegate>

@end

单独创建一个类,来实现复杂的 UI 中的一些视图和逻辑;

协议分发与 “自释放”
@interface ProtocolDispatcher ()

@property (nonatomic, strong) Protocol *prococol;
@property (nonatomic, strong) NSArray *implemertors;

@end

@implementation ProtocolDispatcher

+ (id)dispatcherProtocol:(Protocol *)protocol toImplemertors:(NSArray *)implemertors {
    return [[ProtocolDispatcher alloc] initWithProtocol:protocol toImplemertors:implemertors];
}

- (instancetype)initWithProtocol:(Protocol *)protocol toImplemertors:(NSArray *)implemertors {
    if (self = [super init]) {
        self.prococol = protocol;
        NSMutableArray *implemertorContexts = [NSMutableArray arrayWithCapacity:implemertors.count];
        [implemertors enumerateObjectsUsingBlock:^(id implemertor, NSUInteger idx, BOOL * _Nonnull stop) {
            ImplemertorContext *implemertorContext = [ImplemertorContext new];
            implemertorContext.implemertor = implemertor;
            [implemertorContexts addObject:implemertorContext];
            // 由于分发器只是一个局部变量,将其放到给定的 implemertor 中,等 implemertor 释放是,分发器也会释放掉
            objc_setAssociatedObject(implemertor, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }];
        self.implemertors = implemertorContexts;
    }
    return self;
}

这里将协议和类绑定在一起,待外部传入协议和类之后,分给实现了协议的类去处理,那么怎么知道一个类是否以及实现了协议里面的方法呢,系统以及有了 protocol_getMethodDescription 函数来查看协议中是否有对应的方法,如下

/**
 如何做到只对Protocol中Selector函数的调用做分发是设计的关键,系统提供有函数
 通过以下方法即可判断Selector是否属于某一Protocol
 
 objc_method_description 的两个成员变量分别表示 运行时方法的名字和方法的参数
 */
struct objc_method_description MethodDescriptionForSELInProtocol(Protocol *protocol, SEL sel)
{
    struct objc_method_description description = protocol_getMethodDescription(protocol, sel, YES, YES);
    if (description.types)
    {
        return description;
    }
    description = protocol_getMethodDescription(protocol, sel, NO, YES);
    if (description.types)
    {
        return description;
    }
    return (struct objc_method_description){NULL, NULL};
}

BOOL ProtocolContainSel(Protocol *protocol, SEL sel)
{
    return MethodDescriptionForSELInProtocol(protocol, sel).types ? YES: NO;
}

另外,在 iOS 消息转发的动态特性中,我们可以实现一个类是否满足可以相应该方法。要注意重写 respondsToSelector 方法来判定是否可以相应此方法。

// 一:动态解析
 + (BOOL)resolveInstanceMethod:(SEL)sel
 + (BOOL)resolveClassMethod:(SEL)sel
 
// 二:快速转发
 // 返回实现了方法的消息转发对象
 - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
 
// 三:慢速转发
 // 函数签名
 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
 //函数调用
 - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");

以上则是实现了 庞海礁 提到的协议分发器,此分发器可以将协议分发给多个实现者,如果函数有返回值,则前面返回的返回值会被后面返回的覆盖,即以数组最后一个可以实现该方法的类为准。但是我们需要实现的 A/B Test 是只需要有一个实现者即可,因此 李剑飞 在此基础上,修改了一下分发器的初始化方法

/**
 协议分发器
 @param protocol 遵循的协议;
 @param indexImplemertor AB Test 需要执行的协议实现实例数组下标;
        若传入 对应的 NSNumber 数字, 则调用改实现实例的协议方法;
        若传入 nil,则调用全部的遵循协议的实现实例
 @param implemertors 所有需要遵循协议的实现实例;
 @return 协议分发器;
 */
+ (id)dispatcherProtocol:(Protocol *)protocol
    withIndexImplemertor:(NSNumber *)indexImplemertor
          toImplemertors:(NSArray *)implemertors;

通过传入的 number 来决定具体由哪个 implemertor 去实现此协议,具体可以参考他的 github

 self.delegateSource_A = [UITableViewDelegateDataSource_A new];
    self.delegateSource_B = [UITableViewDelegateDataSource_B new];
    
    // A = 0
    // B = 1
    NSUInteger type = 1;
    
    self.tableView.delegate = ABTestProtocolDispatcher(UITableViewDelegate,
                                                   @(type),
                                                   self.delegateSource_A,
                                                   self.delegateSource_B);
    
    self.tableView.dataSource = ABTestProtocolDispatcher(UITableViewDataSource,
                                                     @(type),
                                                     self.delegateSource_A,
                                                     self.delegateSource_B);

参考资料

  1. iOS A/B Test 方案探索
  2. Protocol 协议分发器
  3. iOS 释放自注销模式设计

相关文章

  • 基于协议分发器的 A/B Test 方案

    最近在看一些博客的时候,发现了一篇 iOS A/B Test 方案探索 ,突然想到在上家公司(做电商的)的时候,接...

  • Protocol协议分发器

    Protocol协议代理在开发中应用频繁,开发者经常会遇到一个问题——事件的连续传递。比如,为了隔离封装,开发者可...

  • MPLS VPN之LDP(2)

    建立LSP有静态和动态两种方法,动态建立LSP需要路由器运行标签分发协议,LDP协议就是最重要的一个标签分发协议;...

  • react 笔记

    方案实现优势缺点基于 Nginx的路由分发通过 HTTP 服务器的反向代理来实现,通过路由前缀的不同将用户的请求指...

  • iOS A/B Test 方案探索

    引子 公元2016年末,2017年初,某做旅行产品的互联网公司内,产品经理疯狂的提 A/BTest 需求,以至于该...

  • iOS A/B Test 方案探索

    转发文章:iOS A/B Test 方案探索[%5Bhttps://www.jianshu.com/p/ba7ba...

  • SVN更新与其他人更新冲突了怎么办?

    假设A、B两个用户,他们分别从svn服务器中检出了test1.txt文件,此时A、B、服务器三个地方的test1....

  • 遨游浏览器每天也可挖矿赚钱

    共生币通过自己独创的POV(Proof-Of-Value)协议进行分发。通过挖矿浏览器进行挖矿,获得分发的共...

  • Always Valid Inference- Continuo

    本篇是Peeking at A/B test——Optimizely解决方案简介的补充,原文参考Always Va...

  • HTTP

    HTTP的本质 HTTP协议是浏览器与服务器之间的数据传送协议。作为应用层协议,HTTP是基于TCP/IP协议来传...

网友评论

      本文标题:基于协议分发器的 A/B Test 方案

      本文链接:https://www.haomeiwen.com/subject/fckkpxtx.html