前言

链式调用(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();

iOS开发之实现方法链调用相关推荐

  1. 【iOS开发】—— 通过URL Scheme调用外部地图软件

    写项目时,涉及到一个导航到球馆的功能,可以选择在app里加一个地图,但是这个方案接入要一定的时间,还会增加APP的内存占用:所以我选择了通过URL Scheme去调用手机已安装的导航软件来进行导航,这 ...

  2. iOS开发笔记--OC工程中调用不了Swift代码

    今天在OC工程里面直接引入了一个第三方的Swift包,结果OC文件里面死活找不到Swift对象.打开 "工程名-swift.h"文件内容似乎是空的.(正常的文件应该会有很多OC方法 ...

  3. iOS开发中使用[[UIApplication sharedApplication] openURL:]加载其它应用

    iOS 应用程序之间(1) 在iOS开发中,经常需要调用其它App,如拨打电话.发送邮件等.UIApplication:openURL:方法是实现这一目的的最简单方法,该方法一般通过提供的url参数的 ...

  4. iOS开发各种权限问题(相机、录音等)

    起因 在iOS开发中我们经常会调用系统相机和麦克风,但是这些权限都是用户可以控制的,当APP没有权限调用这些手机资源,但是我们没有判断,直接去调用了,这样会出现问题. 麦克风权限 我在项目中开发IM的 ...

  5. iOS开发 iap、GameCenter、iCloud、Passbook系统服务开发调用

    iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...

  6. iOS开发笔记--iOS应用架构谈 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  7. iOS开发--iOS应用架构谈 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  8. ios开发学习笔记--调用相册或相机(UIImagePickerController)

    Ios开发相册和相机的使用-UIImagePickerController 在开发中,有时候需要获取用户的相册或者调用相机采集图片,比如APP的头像,此时可以使用UIImagePickerContro ...

  9. IOS开发调用系统相机和打开闪光灯

    IOS开发调用系统相机和打开闪光灯      今天给大家分享一下如何调用iphone的拍照功能和打开闪光灯,有些代码我也不太理解,很多是在网上借鉴其他人的.IOS有两种的拍照和视频的方式:1.直接使用 ...

  10. iOS 开发之调用系统铃声以及震动

    iOS 开发之调用系统铃声以及震动 @interface AlarmClass : NSObject {SystemSoundID soundID; }//调用震动 -(void)systemShak ...

最新文章

  1. HDU 2094 产生冠军
  2. java 并发包学习_Java学习笔记—多线程(java.util.concurrent并发包概括,转载)
  3. python基础30个常用代码大全-Python3列表内置方法大全及示例代码小结
  4. RocketMQ介绍与云服务器安装
  5. [译]GLUT教程 - 键盘高级特性
  6. foxmail邮件加载失败重试_TP5实现邮件发送(PHP 利用QQ邮箱发送邮件「PHPMailer」)...
  7. codesys编程_CODESYS楼宇自动化应用案例:化学系教学楼智能排气系统
  8. 单机环境下(双机或是分布式系统不用考虑这个问题),app_offline.htm是个不错的选择...
  9. nbu备份文件失败,提示信息NBU status: 2074, EMM status: Disk volume is down
  10. 河南省第九届ACM程序设计大赛总结
  11. java自己写母版_Java 创建并用应用幻灯片母版
  12. 后盾网php多少钱_后盾网php视频教程:2020最热的8个后盾网免费php视频教程
  13. Kubernetes Deployment故障排除图解指南
  14. 【ROM制作工具】小白如何进行ROM解包,精简,修改,授权,打包详细图文教程...
  15. 华为鸿蒙系统支持旧机型,华为鸿蒙系统2.0来了! 华为鸿蒙2.0系统支持手机机型...
  16. pyká Unlimited for Mac(项目管理软件)
  17. 网络系统管理模块B考核题目(windows2019)
  18. 自己动手用3D打印出你的个人数学科技馆
  19. 北大国际医院:基于互联网医疗的移动诊疗方案分析与设计
  20. Janino框架初识与使用教程

热门文章

  1. 关于group by 两个或以上条件的分析
  2. Script:收集数据库中用户的角色和表空间等信息
  3. 【Luogu1345】周游加拿大(动态规划)
  4. UVa 12174 (滑动窗口) Shuffle
  5. WP7的Push Notifications
  6. 多目标数据关联基本方法
  7. LDA(线性判别分析或称Fisher线性判别),PCA(主成份分析)代码及表情识别中的应用
  8. 每天一个实用小技巧!教你在Mac上共享WiFi密码
  9. 通过Wi-Fi将iPhone与Mac同步的教程
  10. xcode 重新来过openssl项目历程