iOS开发之实现方法链调用

前言


链式调用(chained calls)是指在函数调用返回了一个对象的时候,使得这个调用链可以不断的调用下去。从概念上可以看做是一环扣一环的铁链,也能被称作方法链调用。



假设需求是在网络请求完成之后先筛选过期数据,然后转换成对应的数据模型进行展示。在Swift中可以直接这么写:


letdataArr = result["data"]as![Dictionary]

self.models = dataArr.filter{$0["status"] == "1"}.map{Model($0)}


而OC的语法决定了这步操作不能像Swift一样简洁,最常见的代码就是这样:


NSArray *dataArr = result[@"data"];

NSMutableArray *models = @[].mutableCopy;

for(NSDictionary *dictindataArr){

    if([dict[@"status"]isEqualToString:@"1"]){

        [modelsaddObject:[[Modelalloc]initWithDict:dict]];

    }

}

self.models = [NSArrayarrayWithArray:models];


对比两段代码,不难看出方法链调用的优点包括:


  • 代码简洁优雅,可读性强

  • 减少了重复使用同一变量的代码量

  • 把复杂的操作分割成多个小操作连续调用


Swift的特性决定了其在链式调用上的先天优势,但是有没有办法让OC也能拥有这种链式调用的特性呢?答案是毫无疑问的,block是一种非常优秀的机制,允许我们使用点语法的方式调用属性block


其他要求


实现链式调用做法并不复杂,但是符合这些要求会让你用起来更加得心应手。譬如:


  • 对block有过足够深的使用和了解

  • 对retain cycle深恶痛疾,网上很多教程实际上存在着循环引用的问题

  • 升级到Xcode8.3以上的版本,理由无他,加强了属性block调用的代码联想


其中第三点是笔者最推崇的要求,要知道8.3之前的链式封装多多少少吃了不少代码联想的苦头


丑陋的数据源


UITableView是个非常牛逼的控件,但是对于开发者而言也并不是那么的友善,甚至有些丑陋。实现一次tableView的代理起码要有以下代码:


#pragma mark - UITableViewDataSource

-(NSInteger)tableView:(UITableView *)tableViewnumberOfRowsInSection:(NSInteger)section{

    returnself.dataSource.count;

}

 

-(UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath{

    UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"reuseIdentifier"];

    /// configure cell

    returncell;

}

 

#pragma mark - UITableViewDelegate

-(void)tableView:(UITableView *)tableViewdidSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    /// do something

}


Protocol是一种非常优雅的设计方案,这点是毋庸置疑的。但即便再优雅的代码设计,在重复的代码面前,也会让人感到丑陋、厌倦。


block相较起代理,是一种更加强大的机制。比前者更解耦更简洁,当然两者的优劣比较不是本文要讨论的问题,通过block调用的返回对象,大可以给NSArray实现这样的代码:


NSArray *dataArr = result[@"data"];

self.models = dataArr.filter(^BOOL(NSDictionary *dict){

    return[dict[@"status"]isEqualToString:@"1"];

}).map(^id(NSDictionary *dict){

    return[[Modelalloc]initWithDict:dict];

});


虽然代码简洁性上仍然差了Swift一筹,但是相较起原执行代码逻辑性跟可读性都强了不少,这部分的代码详见由浅至深学习block(http://www.jianshu.com/p/29d70274374b)


链式数据源的实现


由于谁都可能是UITableView的数据源对象,那么最粗暴的做法是为NSObject提供一个category用来实现相关的数据源并且动态添加属性。但是作为有追求的攻城狮,我们有追求,要优雅,所以这种方式直接cut掉


UITableView的繁杂代码一直是热议的话题之一,在不同的代码架构中有不同的解决方案,比如MVVM中封装一个专门的VM来统一处理这部分逻辑。因此可以提供一个对象作为实际数据源对象跟UITableView之间的中介。由中介实现相关协议方法,然后从实际数据源对象方获取数据


中介被我命名为LXDTableViewProtocolHelper,为了保证能够生成方法链,所有的属性block应当返回中介对象:


@classLXDTableViewProtocolHelper;

typedefLXDTableViewProtocolHelper *(^TVNumberOfSections)(NSInteger(^)(void));

typedefLXDTableViewProtocolHelper *(^TVRowsNumberAtSection)(NSInteger(^)(NSIntegersection));

typedefLXDTableViewProtocolHelper *(^TVDidSelectedCellHandle)(void(^)(NSIndexPath *index));

typedefLXDTableViewProtocolHelper *(^TVConfigureCell)(void(^)(__kindof UITableViewCell *cell,NSIndexPath *index));

typedef LXDTableViewProtocolHelper (^TVBindAndRegister)(UITableView tableView, Class cellCls, BOOL isNib, NSString * reuseIdentifier);


@interfaceLXDTableViewProtocolHelper : NSObject

 

@property(nonatomic,readonly)TVConfigureCellconfigurateCell;

@property(nonatomic,readonly)TVBindAndRegisterbindTableView;

@property(nonatomic,readonly)TVNumberOfSectionssectionsNumber;

@property(nonatomic,readonly)TVRowsNumberAtSectionrowsNumber;

@property(nonatomic,readonly)TVDidSelectedCellHandledidSelectedCell;

 

@end


使用只读属性修饰block之后我们只需重写getter方法返回对应的处理就行了,拿rowsNumber做个例子,按照网上很多教程都会这么写:


-(TVRowsNumberAtSection)rowsNumber{

    return ^LXDTableViewProtocolHelper *(NSInteger(^numberOfRowsInSection)(NSIntegersection)){

        self.numberOfRowsInSection = numberOfRowsInSection;

        returnself;

    };

}


但是实际上这个返回的block是__NSMallocBlock__类型的,这意味着这种做法会让中介被引用。当然笔者没去测试中介是否能正确释放而是直接采用__weak做法,感兴趣的读者可以重写dealloc来检测。最后tableView的协议方法就能被这样实现:


-(void)configureTableViewProtocol{

    WeakDefine

    self.listHelper.bindTableView(_list,[UITableViewCellclass],NO,@"cell").rowsNumber(^NSInteger(NSIntegersection){

        returnweakself.models.count;

    }).configurateCell(^(SingleTitleCell *cell,NSIndexPath *index){

        cell.titleLabel.text = weakself.models[index.row];

    }).didSelectedCell(^(NSIndexPath *index){

        [weakselfupdateInfoWithModel:weakself.models[index.row]];

    });

}


更多


得益于强大的block,即便OC没有Swift那么优雅的高阶函数,依旧能够实现让代码紧凑已读,当然也会提高debug的难度。除了将数据源链式之外,你还可以尝试把网络请求进行封装,做成链式处理,比如笔者的请求代码:


Get(Component(@"user/getUserInfo",nil)).then(^(NSDictionary *result){

    /// request success

}).failed(^(NSError *error){

    /// request failed

}).start();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值