原文:http://www.cnblogs.com/lujianwenance/p/5800232.html
这是一个很蛋疼的过程,先说一下需求,列表页预加载更多(60%)。当我看到这个需求的时候感觉没什么,这个需求很正常。我还是先说下一下分享这个东西的目的,第一、就是让一些新人(主要是某个新人)了解一下,在开发一个新功能的时候,很少有情况是直接就把问题解决的,基本上都是经历了几次纠结的bug或者说是出乎意料的情况,这时候不要烦操,大脑不要混乱,要冷静的一步一步的分析这个bug或者意外的原因,发现并找到问题就成功了一半了。当然如果这个功能被你直接搞定了,只能说明你对这个需求很熟悉或者这个需求很easy。第二、简单的说一下ios黑魔法method-swizzling的使用。废话不多说,正文开始了。
一、分析问题
看到这个需求,我首先想到的是60%的时间点在哪,两个想法,一个是scrollview滑动到的位置,也就是offset;另一个是tableview滑动到的indepath。下面是分析的过程:1、这些列表页都有一个特点就是拥有上拉加载更多和下拉刷新,上拉加载更多的UI展示代码基本上是在tableview的tablefooterview中写的,60%加载数据跟footer没有太大的直接关系。2、scorllview的offset的值会根据你滑动的速度不同,得到的结果相差就比较大,所以在60%处预加载的偏差也会更大。3、预加载功能跟当前ViewController好像更合适,需不需要加载其实就是当前的ViewController来决定的。4、在tableview的cellForRow中判断当前显示到什么位置会更准确,滑动到60%就是滑动到了,没有什么争议。通过上面的分析,我决定使用在BaseViewController中确定预加载的时间点。(这只是我的理由,如果大神们看到有不合理的地方,请指正)
二、解决问题
找到了位置,下面就开始撸代码了,这段代码肯定是写在父类的,所以第一个想法就是父类里面实现tableview:cellforrowatindexpath:这个方法,然后所有的子类重写这个方法的时候先调用一下super的方法,还是感觉这样写改动的地方太多,而且让使用者很奇怪,感觉这是什么东西,怎么使用tableview的代理方法还需要调用super的?到这里,我想到了一个“很坑的”方法,method-swizzling。
当按照网上大部分的教程中写的那样,在+load方法中添加我们的交换方法,然而运行的时候并没有效果,这时可能我们就会意识到,我们交换的方法并不是系统提供给我们的方法,所以load时交换没有任何效果。我将他加在了初始化方法中,这是成功的运行了。没有代码,好难受:
preLoadingScale是在加载更多的请求返回数据中计算下次需要自动加载的位置所占全部数据的比例,isDragingDown是用来判断从上向下滑动,感觉已经实现了,但经验告诉我们,这只是刚刚开始。
问题一、通过测试发现,第一次进入页面可以,第二次进入页面就没有效果了,第三次可以......很有规律的bug,这时充看一下代码,每次初始化一个对象交换一下方法,第一次交换,第二次交换......好像又交换回去了。对,确实是这样的,在交换方法里面加上dispatch_once:
再运行,OK了。
问题二、当第二次进入时另一个子类(代码是在父类中写的)的时候,没有作用。分析问题,交换的代码只执行一次,当第二次进入的是另一个子类的时候并没有交换当前类的方法,所以没有效果。解决问题,问题一的解决方案不合适,这时需要考虑这两种情况,这次修改的结果是:
重复上述的测试操作,没有问题了。
问题三、写完这个代码总感觉怪怪的,如果没有执行dealloc方法,方法还是没有被交换回来。所以想到了一个测试方法,进入一个子类中,没有问题,这个vc继续使用这个vc推出另一个子类,app crash了。分析问题,当进入第一个页面的时候交换了代理方法,继续进入另一个子类交换方法时,交换的是当前vc的tableview:cellforrowatindexpath:和之前那个页面的tableview:cellforrowatindexpath: ,所以在执行到第二个页面时,tableview的代理方法走到了第一个页面的代理方法中,导致了crash。解决问题,此时似乎问题越来越复杂了,从头到尾发现了这个多问题,都没有被解决,反而问题越来越多。其实不是这样的,这时候一定不能焦操,我们继续,从上面分析问题出现在没有及时的将方法交换回去,那么我们应该很容易想到这样写:
重复测试,没有问题了。
三、测试
每次做完一个功能,一定要自己先测试一下,因为写代码的时候是按照自己的“正常思路”在写,而BUG总是出现在一些“不正常”的地方,大多数人都会经历类似于这个过程的修改,当然不可否认,大神们根据自己的经验或者对知识的深入理解回跳过很多坑,但是坑总是存在的,总有“失误”的那一天。
四、总结
开头已经说了本文的目的,就是告诉新人(某个人)在遇到问题是不要烦、不要怕,而是要先复现bug,找到问题的原因 ,然后想解决的办法,不要自乱阵脚。(突然发现本文与题目好像没有多大的关系。。。。。。)还是需要总结一下method swizzling的使用点:
1、使用系统预制的方法,可以将交换方法写在+load方法中,但是当两个方法都是自己实现的方法时,+load中书写就没有效果了。
2、使用method swizzling 会影响整个类的所有子类,使用的时候需要特别小心,使用不当很容易造成“奇怪”的bug。比如在上面的例子中,所有的问题基本上都是使用了交换而没有恢复的情况。
3、不知道这样用是否合适,希望能看到的大神给出建议,谢谢。
====================华丽的分割线=============================
学习知识真的是温故而知新。当时使用了这个方案之后,由于展示这个页面的地方不是固定的,需要根据后台返回的数据来判断是需要展示在二级及二级以后的页面中还是展示在Tabbar的页面中,而且项目中存在很多以前自定义跳转动画的逻辑代码,导致viewController在调用viewWillAppear和viewWillDisappear的顺序上可能会乱序,所以放弃了这个方案(当然,后期经过考虑感觉写在footer中比这个方案更合适,这个合适的问题就不讨论了,下面主要是对这个方案的完善做分析),在过去两个月的时间之后,今天有时间回头看了一下这个问题。在介绍这个问题之前先说一下应用的场景,需要实现的viewController有一个封装了tableview的父控制器,使用这个预加载功能的页面都是继承自这个viewController,所以网上大部分的介绍Method-swizzling的文章所说的在+load方法中实现是不行的(load中拿到的是当前类的方法,无法获得父类或者子类中的方法,无法实现交换)。最终的实现是,对象实例化之后,先对子类中添加自己的需要交换的方法,然后在交换,这样就解决了上面交换之后还得交换回来的问题,因为其实之前的方案是将子类中的方法和父类中的方法交换,影响到了父类,导致每次生成一个新的子类时都会出现问题。现在就剩下一个问题了,那就是将交换过的类进行记录(我是写了一个manager单例来管理),如果当前类已经交换过了就不需要再次交换。到这里这个问题就被完整的解决了。2016.11添加
- Objective-C的hook方案(一): Method Swizzling
Objective-C的hook方案(一): Method Swizzling 转自:http://blog.csdn.net/yiyaaixuexi/article/details/9374411 ...
- ios逆向工程-内部钩子(Method Swizzling)
Method Swizzling(方法调配) 怎么说呢,先了解什么是钩子为什么用钩子,学过C++的朋友应该清楚,hook就是用来获得(截断/改变)底层调用的方法.这样我们可以自由的修改或者读取一些想要 ...
- runtime 第四部分method swizzling
接上一篇 http://www.cnblogs.com/ddavidXu/p/5924597.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ...
- Objective-C Runtime 运行时之四:Method Swizzling
理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...
- 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入
概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为“移花接木 ...
- Method Swizzling
学习博客: http://www.cocoachina.com/ios/20160121/15076.html (这个作者太牛了,写了我一直想知道的类簇的swizz方法) 一. 一般的swizz 先给 ...
- Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改
功能:修改父类不可修改函数方法,函数方法交换 应用场景:假如我们使用的他人提供一个的framework,.m已被打包成二进制.a无法修改源码,只留下.h头文件,那假如代码中某个函数出现了问题可以通过这 ...
- Method Swizzling (方法调配)
Method Swizzling是改变一个selector的实际实现的技术.通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现. 例如,我们想跟踪在程序中每 ...
- iOS中AOP与Method Swizzling 项目中的应用
引子:项目中需要对按钮点击事件进行统计分析,现在项目中就是在按钮的响应代码中添加点击事件,非常繁琐.所以使用了AOP(面向切面编程),将统计的业务逻辑统一抽离出来. 项目中添加的开源库:https:/ ...
随机推荐
- linux 开机自动挂载ntfs盘
1) 查看盘符UUID vellbibi@vell001:~$ sudo blkid [sudo] password for vellbibi: /dev/sda1: UUID="bce9e ...
- HDOJ-ACM1012(JAVA)
这道题很简单,主要是弄懂题意和注意输出: 输出的完整结果如下: n e - ----------- 0 1 1 2 2 2.5 3 2.666666667 4 2.708333333 5 2.7166 ...
- 小波变换和motion信号处理(二)(转)
写的太好,这是第二篇:http://www.kunli.info/2011/02/18/fourier-wavelet-motion-signal-2/ 这是<小波变换和motion信号处理&g ...
- Redis压缩列表原理与应用分析
摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...
- light oj 1354 - IP Checking
1354 - IP Checking PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB An I ...
- Android框架结构图
- 使用dispatch_once:创建单列
无论是爱还是恨,你都需要单例.实际上每个iOS或Mac OS应用都至少会有UIApplication或NSApplication. 什么是单例呢?Wikipedia是如此定义的: 在软件工程中,单例是 ...
- 在 Android 的 IM 应用中使用 asmack 库实现用户头像的传输(基于VCard协议)
http://quietmadman.blog.51cto.com/3269500/1359495 Beem http://beem-project.com/projects/beem/reposit ...
- c#中cookies的存取操作
在客户端创建一个username的cookies,其值为gjy,有效期为1天. 方法1: Response.Cookies["username"].Value="zxf& ...
- 开启一个指定action的Activity
开启一个指定action的Activity如果你想要使用android系统自带的一些服务,如照相机,通信录,打电话等,那么你就得需要知道对应服务(也就是在AndroidManifest.xml中< ...