zoukankan      html  css  js  c++  java
  • 在项目中谨慎为系统类添加分类!!!!!

    结论:

    1、坚决杜绝为系统类做方法交换(见到【class_replaceMethod】格杀勿论!)

    2、为系统类添加分类时候,属性和方法名必须加上【世上独一无二】的前缀,避免冲突和混淆。


    之所以让我对上述行为恨之入骨是因为,今天为了一个bug,我花了将近半天时间苦苦追寻原因。

    我只是使用了一个简简单单的UIImagePickerController的拍照的功能界面,奇葩的现象是,点击快门按钮时---可以看见界面中的按钮发生了视觉上的响应,但是却没有功能上的响应(按道理,我这边按下按钮的时候,拍照就会完成输出图片数据)。

    我的整个思考过程是这样的:

    点击没功能反应?难道有谁把这个类中的响应方法重写了?

    ---寻找UIImagePickerController在整个项目中的出现,看有没有对它做分类,或者是子类化。结果是没有的!

    那是不是关于UIImagePickerController这个类,随着iOS的SDK的更新,我有些属性或者方法需要适配下?

    ---我用iOS10.2和iOS11.2和最新的iOS11.4都看了一遍,都有这个问题。难道从iOS10开始就要有些跟之前不一样的适配需要做?我翻看相关的适配博客,没有发现!

    难道是我对事件响应链做了一些调整?导致事件被阻断?

    ---我回头看了一眼UIImagePickerController对象创建后使用的是模态出来的,一个简单的展示链,没有问题!

    难道多线程问题?

    ---NONONO!我核对了下代码,整个过程都在主线程中,至于就算UIImagePickerController里面的处理上开了子线程,那也不归我们管,它暴露出来的API肯定是在主线程的。

    那就见鬼了~但是,不对啊,就这个简单的UIImagePickerController,不至于啊!

    ---我应该是知道肯定是项目中的其他SDK的环境影响到了它,但是会是什么呢?为了更加确定我的这个想法,给自己继续追寻原因的信心,我新建demo,这块代码原样放入。卧槽,完美运行。

    那行,我这样的话,我要一查到底!

    ---能够引起这个问题的全局原因,那么就是项目的配置数据有误,那么就是分类的原因。

      项目的配置数据就是那些,最多就是在info文件中说明下使用相机的原因,方便获取用户的授权。因此我肯定,这块没有问题。

      分类的话,我已经确定了没有UIImagePickerController的分类。那么肯定就是其他系统类的分类了。

      首先,添加分类的不可控性体现在:

    (1)如果在分类中重写类的方法,分类的重写优先级是最高的。  

    (2)如果系统对UIImagePickerController添加了一些分类(包括不暴露在API中的),刚好又与项目中对其的分类方法名重复,会后入为主的。

    (3)另外分类是会在编译器就全部加上的,如果在分类中对类本身做的处理是会影响到类本身的。也就是说,如果对类中的方法做了方法转移的处理,那就无形中影响了。

    于是我赶紧搜索方法转移的class_replaceMethod方法名有没有在项目中出现。果然,项目中对UIButton的分类中重写了+load类方法,在改方法中做了方法转移!

    正如前面分析的,重写+load方法的优先级:分类中>子类中>类本身。

    并且重写的是+load这个方法,完全可以做到悄无声息。

    为了进一步验证就是这个原因,我直接将这个分类的实现方法注释掉,然后运行项目~【method_exchangeImplementations完美运作!!】


    刚刚时候的是相当于反编译的方式把问题的根源找到了,现在我需要的是使用顺推的方法,把问题的出现原因梳理清楚。

    通过查看UIButton的这个分类知道,它是将@selector(sendAction:to:forevent:)这个方法替换掉了。sendAction:to:forevent:方法中实际调用的是objc_setAssociatedObject,替换后的方法,在其中加了一个计时器,使得规定时间内,只能objc_setAssociatedObject调用一次。

    这样的做法,应该是为了防止button高频按动而做的改动。

    然而,UIImagePickerController功能界面中的快门按钮,实际上是在拍照功能时,按住快门键不放,可以实现高频连拍的功能(我试了下最多时999张),这样的话,就很好解释通了。虽然,按住快门键按钮不放是一个“长按”手势,但是其内部的实现肯定是高频的调用@selector(sendAction:to:forevent:)这个方法。说到这里,我得说明下,虽然长按手势和单点手势表面上的确是不一样的,但是其内部都调用了@selector(sendAction:to:forevent:)这个方法。因此,之前写button这个分类的目的虽然是防止用户高频的单击按钮,但是现在用户虽然不是高频的单击,而是长按,但是都调用的是@selector(sendAction:to:forevent:)这个方法。毕竟,当初为了防止用户高频单击,是替换掉了@selector(sendAction:to:forevent:)这个方法。因此,谜底揭开了,整个离奇的故事真相大白~

  • 相关阅读:
    sujection重构
    serialize存入数组
    migration integer limit option
    FineReport——FS
    Oracle事务处理
    FineReport——发送邮件
    FineReport——登录不到决策系统
    FineReport——JS二次开发(复选框全选)
    FineReport——JS二次开发(局部刷新)
    FineReport——JS二次开发(工具栏按钮事件及说明)
  • 原文地址:https://www.cnblogs.com/cchHers/p/9397085.html
Copyright © 2011-2022 走看看