博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
逆向微信-分析学习微信是如何快速构建静态TableView界面的
阅读量:6227 次
发布时间:2019-06-21

本文共 5083 字,大约阅读时间需要 16 分钟。

背景:

在搭建APP静态TableView界面时,都是每个vc对应创建一个UITableView,然后实现UITableViewDataSource、UITableViewDelegate等方法,这样的开发方式有几大弊端:

1)效率不高,每个界面都得创建,实现协议。
2)cell的点击事件区分时需要一大堆的if/else。
3)界面元素变化时,维护起来非常蛋疼,需要修改好几个地方的if/else。
在分析完微信后,发现微信搭建静态TableView页面时,并不会出现上面几个问题,搭建非常easy,所以决定将学习到的思路分享出来大家一起交流学习。

分析过程中用到的工具:

1.一台越狱的5s

2. dumpdecrypted(砸壳)
3.class-dump(导出头文件)
4.IDA(反汇编)
5.cycript(调试)

逆向的基础知识就不概述了,本文章主要是对微信进行静态分析

一、找到关键类

1.MMTableViewInfo

通过观察多个静态页面的vc,发现vc里没有直接创建UITableView,而是通过MMTableViewInfo这个类创建的,MMTableViewInfo里有个_tableView成员变量,并实现了UITableViewDataSource、UITableViewDelegate,所以无误。下图是MMTableViewInfo头文件截图部分内容:

2.MMTableViewSectionInfo

通过观察,很容易看出MMTableViewInfo中的成员变量_arrSections是_tableView的数据源,调试打印其元素是MMTableViewSectionInfo对象。下图是MMTableViewSectionInfo头文件截图部分内容:

3.MMTableViewCellInfo

通过观察,猜测MMTableViewSectionInfo中的_arrCells应该是每个section中的cell数组,调试打印其元素是MMTableViewCellInfo对象。下图是MMTableViewCellInfo头文件截图部分内容:

二、通过反向推理,正向梳理逻辑

1.观察MMTableViewCellInfo头文件,通过fCellHeight、cellStyle、accessoryType、+ (id)normalCellForTitle:(id)arg1 rightValue:(id)arg2这几个属性和方法,应该可以想到,这个类就是为cell准备数据的。

2.观察MMTableViewSectionInfo头文件,- (void)addCell:(id)arg1;通过该方法添加cellInfo到_arrCells里构成了一个组的数据

3.观察MMTableViewInfo头文件,- (void)addSection:(id)arg1,可以想到是添加sectionInfo到_arrSections里构成了UITableView的数据源

现在知道了三者的构成关系,接下来的重点就是去分析其内部是如何实现的了。

三、分析内部实现

接下来通过IDA反汇编工具,查看每个类具体实现的伪代码

1. MMTableViewCellInfo的实现

先看下伪代码(因封装的方法较多,这里只分析一个方法):

分析转化为oc代码是这样的,类名前缀我使用了LY,注意:demo里对基础的cellInfo做了一层封装。
第二个方法有SEL和target,这里是微信对cell的选中事件进行了处理,使用了target/action方式,所以监听cell的点击时不需要使用代理,使得每个cell有自己的action,即做到了解耦,又不用写一堆的if/else了。
微信对一些信息使用kvc进行存取,比如这里的title(textLabel)和rightValue(detailTextLabel),存取的方法在MMTableViewCellInfo的父类MMTableViewUserInfo里。

2. MMTableViewSectionInfo的实现

这个类的实现相对简单,现在只看cell是如何添加的。

///添加cell- (void)addCell:(LYTableViewCellInfo *)cell{    [_arrCells addObject:cell];}复制代码

3. MMTableViewInfo的实现

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{    return _arrSections.count;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    LYTableViewSectionInfo *sectionInfo = _arrSections[section];    return [sectionInfo getCellCount];}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{    LYTableViewSectionInfo *sectionInfo = _arrSections[indexPath.section];    LYTableViewCellInfo *cellInfo = [sectionInfo getCellAt:indexPath.row];    return cellInfo.fCellHeight;}复制代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{        LYTableViewSectionInfo *sectionInfo = _arrSections[indexPath.section];    LYTableViewCellInfo *cellInfo = [sectionInfo getCellAt:indexPath.row];        NSString *iden = [NSString stringWithFormat:@"LYTableViewInfo_%zd_%zd", indexPath.section, indexPath.row];    LYTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:iden];    if (!cell) {        cell = [[LYTableViewCell alloc] initWithStyle:cellInfo.cellStyle reuseIdentifier:iden];    }        cell.accessoryType = cellInfo.accessoryType;    cell.selectionStyle = cellInfo.selectionStyle;    cell.textLabel.text = [cellInfo getUserInfoValueForKey:@"title"];//通过LYTableViewCellInfo 父类方法kvc获取到    cell.detailTextLabel.text = [cellInfo getUserInfoValueForKey:@"rightValue"];//通过LYTableViewCellInfo 父类方法kvc获取到        return cell;}复制代码
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    [tableView deselectRowAtIndexPath:indexPath animated:YES];        LYTableViewSectionInfo *sectionInfo = _arrSections[indexPath.section];    LYTableViewCellInfo *cellInfo = [sectionInfo getCellAt:indexPath.row];        id target = cellInfo.actionTarget;    SEL selector = cellInfo.actionSel;        if (cellInfo.selectionStyle) {        if ([target respondsToSelector:selector]) {            [target performSelector:selector withObject:cellInfo withObject:indexPath];//创建cellInfo时,target传递并实现了SEL事件,这里就会发送这个消息,从而实现cell的点击事件        }    }}复制代码

该类里的数据来源就是MMTableViewSectionInfo和MMTableViewCellInfo,前面构建好了这两,这里直接就能用了。 看下最简单的调用示例:

#pragma mark - Creat View- (void)creatTableView{    _tableViewInfo = [[LYTableViewInfo alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];    [self.view addSubview:[_tableViewInfo getTableView]];        //cell数据    LYTableViewCellInfo *noactionCell = [LYTableViewCellInfo normalCellForTitle:@"无点击事件" rightValue:@"没有"];    LYTableViewCellInfo *actionCell = [LYTableViewCellInfo normalCellForSel:@selector(actionCellClick) target:self title:@"有点击事件" rightValue:@"" accessoryType:UITableViewCellAccessoryDisclosureIndicator];        //section数据    LYTableViewSectionInfo *sectionInfo = [LYTableViewSectionInfo sectionInfoDefaut];    //添加    [sectionInfo addCell:noactionCell];    [sectionInfo addCell:actionCell];    [_tableViewInfo addSection:sectionInfo];        //刷新    [[_tableViewInfo getTableView] reloadData];}#pragma mark - Event- (void)actionCellClick{    NSLog(@"点击了actionCell");}复制代码

通过上面一段代码实现如下:

总结:

以最简单最基础的案例介绍了微信的构建方式,此方式构建满足了组件的可复用性、可维护性、高效性。 这里只是做最简单介绍,大家可根据自己的业务需求对相应的方法做调整,做扩展。

仓库里两个Demo,一个是最基础的组件,一个是稍微完善的组件

转载地址:http://spnna.baihongyu.com/

你可能感兴趣的文章
Linux虚拟化技术KVM、QEMU与libvirt的关系(转)
查看>>
Ceph分布式存储-原理介绍及简单部署
查看>>
MYSQL数据库设计规范与原则
查看>>
UWP: 实现 UWP 应用自启动
查看>>
Windows内核之进程的终止和子进程
查看>>
Python 文件 readline() 方法
查看>>
String,到底创建了多少个对象?
查看>>
linux查找目录下的所有文件中是否含有某个字符串
查看>>
UWP 手绘视频创作工具技术分享系列 - 有 AI 的手绘视频
查看>>
各行业最受欢迎的编程语言,硬件最青睐C和C++
查看>>
监听用户的后退键,解决部分浏览器回退的bug
查看>>
Vivado+FPGA:如何使用Debug Cores(ILA)在线调试(烧录到flash里可以直接启动)
查看>>
[Preference] How to avoid Forced Synchronous Layout or FSL to improve site preference
查看>>
【laravel5.4】php artisan migrate报错:Specified key was too long; max key length is 767 bytes
查看>>
[转]外贸出口流程图
查看>>
微信小程序onLaunch修改globalData的值
查看>>
php实现简单算法3
查看>>
Always run a program in administrator mode in Windows 10
查看>>
打陀螺
查看>>
tcp echo server libuv
查看>>