每天梳理1~2个问题,坚持一下(四)

新开一篇。。。看了下已经有3个月了,还是挺不错的。。。

19.08.02

用分类or工具类

新项目做了一段,遇到一个思考性的问题,AppDelegate中的代码,是分类来分散还是工具类来分散比较好?这里抛开了分类对加载时的影响,仅从设计上来说。

这个问题想了半天,抛开原始方法覆盖来说,分类能做的工具类也能做,难道没有什么差别吗?今天上午和群友讨论了一些,发现对于Appdelegate来说,他们有用工具类也有用分类的,没有一个固定的用法,因为分类和工具类基本上能相互替代。中午睡醒的时候豁然开朗,在讨论分类和工具类为什么没有太大区别的时候这里有个非常重要的前提是:Appdelegate是个单例!想明白了前提条件的不同,基本上就能梳理通了。

对于一般类型的分类例如UILabel来说,我添加了方法A,那么UILabel的对象都具有了A方法的能力,这里用工具类也是可以的,但是需要传入对应的对象才能引用到,使得耦合性很高。功能上虽然两者都是可以实现的,但是从设计上来说,单一职责的低耦合分类模式,显然远胜于职责略显杂乱高耦合的工具类模式。

对于单例类型来说,我可以随时随地拿到这个对象调用这个对象的方法,所以即使在我的工具类的接口里我也无需预留引用参数,这样一来就无须考虑耦合性问题,所以看起来分类和工具类几乎可以相互替换,分类的优势并不明显。

19.08.05 + 19.08.06

做一个渐变色的折线图

两天合一了:https://www.jianshu.com/p/1aec88fb607f

19.08.07

不规则图形处理

分为两部分吧,一部分是不规则图形的绘制,另一部分是对不规则图形内容进行处理。

对于绘制来说,CAShapeLayer + UIBezierPath无疑是处理不规则图形的好帮手,在不需要获取绘制上下文的一般情况下,Layer的绘制远胜于调用draw:rect:。不规则图形的难点主要是坐标的定位与换算,还可能涉及一些公式的运用,可以说是对path路径能力的考察,对于一些比较复杂的图形可能需要一定的数学基础,例如贝塞尔曲线控制点的寻找,正弦曲线的运用等等。

对于对不规则内容处理来说,我们可能进行的不单单是颜色的填充,还可能有渐变填充,分割填充,填充动画等等,对于这部分,我觉得mask遮罩效果能完成80%的需求。mask是一个非常神奇的属性,而这个属性还是CAShapeLayer的对象,这就意味着我的遮罩效果可以是任意的不规则图形,同时它给了视图一种揭开画布的效果,这个效果可以达到简单的动画效果。

周末有空整理一份比较综合的例子。

19.08.08

swift解决OC中的组合问题

虽然平常也用swift写东西,但是大牛一直说要转为swift的思想而不是swift的代码。
最近也查看了不少资料,swift面向协议的思想内容确实不少,值类型的结构体,加强版的协议,组合化的设计思路。。。毕竟是颠覆了OC的整个架构体系,循序渐进先选个协议来入手吧。。。
如何运用swift中的协议做代码的架构和设计,而不是一味地翻译OC的语言。我觉得首先要了解swift协议中的特性:

swift中的协议:
1.可以提供默认实现
2.可以指定服从协议的对象类型
3.可以添加变量
4.协议可以继承
5.协议用来做组合的设计

还有一点比较重要:swift中协议特性无法与@objc标识共用

OC中的协议,起到的往往是事件的传递和值的传递,因为设计上的限制,所以没有太大的发挥空间;而swift中的协议,自从加强版(可以给出默认实现)推出以来,可以说是完全建立起来了一套和OC不同的设计理念,所以swift不单单是从代码上和OC产生了差别,更从思想上与OC脱离。

由于OC是继承树的设计体系,所以我们在设计的时候经常遇到的一个问题是:如何合理地抽象?
很多抽象并不是“合理”的,而是为了达到代码复用。当你发现父类中各种变量和方法是很多子类不需要的时候,你就知道你这个抽象继承的运用是个“不合理”的。这个现象普遍地存在。如果再次妥协,我们把一些方法会做成工具类做调用,似乎可以避免超级父类的出现。但其实这样的做法也只是对代码的一个分散,并没有从根本上解决我们想要什么。

那么我们想要什么呢?不是继承,而是组合。

我需要的是插件般的拼装,各司其职组合在一起运转,而不是一层又一层的继承来获得运转的能力。OC中对组合的友好度显然非常低,但swift的协议搞定了组合的方式。

结合最近的一个动画小场景,我用swift的模式重写了一下有问题的部分,明天整理一下。

19.08.09

swift组合模式的设计应用
写了个swift版本的组合模式应用:https://www.jianshu.com/p/ca6d08cbf5f5

19.08.12

使用KVC修改组件隐藏属性的影响

朋友最近在加班,因为iOS13的系统变动有点大,有一部分比较严重,用KVC修改控件的属性这部分,13的系统把有些控件的结构和属性名称变了,导致修改崩溃。

这让我想起了去年还是前年,在修改navigationBar的时候我用KVC直接找到隐藏的属性修改其颜色。用起来美滋滋,又快又准,但在苹果升级了一次系统之后,navigationBar层级出现了变动,之前的KVC失效且崩溃,加了一晚上班修改BUG寻找替换方案然后发版,从这之后,我就很少用KVC的方式去修改系统控件的隐藏属性,存在未知的系统升级风险。

这种影响不单单是在用KVC的情况下,也包括了其它通过字符串获取信息的方式,例如:分类覆盖原有属性、字符串反射属性、字符串反射方法等。这些方式的不稳定性出现在系统的升级迭代中,有很多是不可预期的。所以使用KVC的地方一定要多加测试,并且加上系统版本的限制。

当然,KVC这种方式在我们自己创建的类中还是很有用的,例如swift中model有id,OC引用时给id赋值时KVC就能很好解决。这种在我们自己创建的类中没有太大的问题,但是需要在使用KVC的地方添加好注释,在后期改动变量名或者方法名时能一起处理使用了KVC的地方。

19.08.13 + 19.08.14

整理了一份波纹扩散效果

总结了思路和遇到的问题,并做了Demo,https://www.jianshu.com/p/f71087567955

19.08.15

写点关于MVVM的

苹果的MVC在轻量级的应用上还是很不错的,毕竟是经典的架构模式。但是苹果推荐的MVC在苹果开发上有个比较尴尬的地方:C和V总是一起出现,ViewController内部总是镶嵌了一个View。这就使得原本的M-V-C各自从一开始就没有独立开来,C和V混在了一起。其实我感觉如果是独立开来的设计,C中似乎也没有那么混乱不堪(但可能代码量就上去了)。

MVVM相比MVC,其实也没有从拆离V和C下手,而是引入了一个VM来分离职责。由于V和C还是没分开,所以这里直接全部当做V,VM承担了原MVC中各部分超出自身但又无处安放的职责。

那么是view就可以来个vm么?

我们需要先搞清VM有什么作用:
1.处理VC中的一些逻辑;
2.处理V中的响应
3.M和V之间的桥梁
4.其它杂事

VC中镶嵌的view一般是需要的。因为VC中嵌套的view会有很多逻辑事件和响应事件要处理,这些任务就要交给VM去搞定。就算这个页面只单纯地展示内容,对数据的请求和处理也应该交由VM(其实这里用MVC更加简洁方便,只是为了说明一下VM的作用)。

单独的view不一定需要。例如cell中的视图,如果展示内容无需逻辑判断,VM是无需的,相反,如果Model中的一个字段有若干种状态,状态又对应不同的展示,那么这个cell就需要一个VM帮他搞定逻辑上的事件,VM去处理各种if逻辑,cell只关心逻辑处理的结果。

19.08.16

@IBInspectable

@IBInspectable的作用是在XIB的面板上添加我们的设置,像下面这样:

@IBInspectable var FitScreen : Bool {
        set {
            objc_setAssociatedObject(self, runtimeKey.key, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
        
        get {
            return objc_getAssociatedObject(self, runtimeKey.key) as? Bool ?? false
        }
    }

回到XIB面板中我们可以在属性选项卡下看到这个代码中的属性,这样我们就可以在XIB面板中直接对这个属性值进行设置:


每天梳理1~2个问题,坚持一下(四)_第1张图片
XIB面板属性设置

这个设置是有局限性的,只能设置非容器类的类型,例如String、bool、Int等。

19.08.19

xib如何按比例缩放

如果以苹果8的屏幕长宽为基准,在不同的机型下进行一个长度的缩放,例如在苹果8上10的长度在苹果X上则为11.04((414-375) / 375 * 10)。

在纯代码下我们可以很容易写个宏解决这个问题,那么在XIB下如何搞定这个问题呢?默认的XIB面板上是没有给我们去配置这些比例缩放的地方的,所以我们需要自己手动写个代码修正一下。

参考了群友的建议和网上的一些方案,个人觉得采用分类的方式是比较好的,像下面这样:

- (void)awakeFromNib {
    [super awakeFromNib];
    // 线 navBar TabBar // || 49 == self.constant || 64 == self.constant 这里不用导航
    if (0.5 == self.constant) {
        
        return;
    }
    
    // width
    if (self.firstAttribute == NSLayoutAttributeLeading || self.firstAttribute == NSLayoutAttributeTrailing|| self.firstAttribute == NSLayoutAttributeCenterX || self.firstAttribute == NSLayoutAttributeWidth) {
        
        self.constant = NB_WIDTH_SCALE*(self.constant);
    }
    
    // height
    if (self.firstAttribute == NSLayoutAttributeTop || self.firstAttribute == NSLayoutAttributeBottom || self.firstAttribute == NSLayoutAttributeCenterY || self.firstAttribute == NSLayoutAttributeHeight) {
        
        self.constant = NB_HEIGHT_SCALE*(self.constant);
    }
}

19.08.20

Xcode缓存导致编译异常

像下面这样的错误:


异常.png

第一次遇见这种,当我编译工程时Xcode莫名其妙自己中断并报了一个error(这个error忘记截图了),再次编译时就报这种错误。
当我尝试换个工程编译的时候,还是会在import系统库的地方报错,挺莫名其妙。一开始我以为是改动到了系统的文件,后来发现并不是,系统文件部分都有加锁定。那只能根据报错原因来寻找了,虽然这个原因并没有见过,但是既然是个路径,那么先去看看吧。找到对于的文件路径,我发现从2HVOH...这个缓存文件之后的都是可以删除的,管他三七二十一直接删了得了。删完再运行。。。居然就OK了。。。应该是缓存出现了异常导致的问题。

这种问题还可能由于MacClean导致,MacClean清理默认带上Xcode这些缓存文件,需要手动去掉一些。

19.08.21

分享一个完整的FFMpeg编译时遇到各种动态库问题的解决方式

我也是偶然在解决环信的一些问题时发现的,觉得还不错,分享一下:
FFMpeg编译动态库

我有个工程中的环信很奇怪,是从另外一个工程中的环信完全剥离出来的,但是就是真机无法运行,报错明显是动态库的问题,但就是不知道哪个动态库,两个工程完全一样的配置有一个就是跑不起来。想到这部分是由于FFmpeg方面的报错,于是就从这方面的编译入手,根据文章的指引添加libz.1.2.5后OK,但另外一个工程中并没添加这个动态库却也能跑起来有点奇怪。

19.08.22

做一个映射关系表来展示信息

工程中有很多地方需要大量展示信息,像人员信息这样的列表一样,样式简单但是比较冗杂,所以我考虑做一个映射关系表来搞定这部分。

由于表这部分并没有放到后台,所以以下都基于在本地的操作。

那么这个表的设计应该是这样的:

本地需要展示的title -> 后台对应的key值

像这样:

姓名 : uesrName

并且本地需要展示的不同的title名称可以对应不同的key值:

用户姓名 : userName
姓名 :name
客户姓名 : name

我们可以把所以需要展示的字段信息全部维护到这个表中。
操作顺序如下:
1.准备好要加载的内容数据,例如["姓名","性别"],转换成通用展示模型model放入数组,这个时候只有displayTitle,同时加载本地这个映射关系表;
2.将后台的数据A转为对应的模型A;
3.遍历装有通用模型的数组,根据映射关系从姓名拿到userName字段;
4.根据userName字段,从模型A中,valueForKey,拿到value,注意这一步需要重写找不到Key的方法,同时处理复杂的需要逻辑判断的value也是在对应的模型中操作,例如处理字符串转时间,处理多个value拼接等等,总之返回的value就是要展示的value,有点想viewModel的味道;
5.这个时候我们数组中的通用model已经拿到了displayTitle所对应的value,按常规展示到cell上即可;

这种方式对展示类的处理非常舒适,如果把映射关系表放到后台,并且对应页面要展示数据标题也放到后台,就能完全做到动态化了。

19.08.23

处理framework中对三方库的依赖方式

先记录下问题,目前要采用pod依赖的方式,但是测试的时候还是有一些小问题。有空再修正一下。

19.08.26

字符串与NSDateFormatter

在将字符串以formatter指定格式变为NSDate类型时,指定格式必须要符合字符串的当前格式。

eg:字符串"2019-08-21"以"YYYY-MM-dd"变为NSDate类型时是没有问题的,但是以"YYYY-MM-dd hh:ss:mm"进行转换的时候就不行,这个字符串并不具备当前的formatter格式。
如果要将字符串"2019-08-21"转为字符串"2019-08-21 00:00:00"这种形式,需要两个formatter进行格式,第一个变为"2019-08-21"的NSDate类型,这个时候任意的formatter都可以使用,第二个就可以以"YYYY-MM-dd hh:ss:mm"进行转换。

19.08.27

HTTP与业务流的分割
做了很多个项目,我发现很多后台在设计接口的时候,很多时候会把HTTP的逻辑和业务流程的逻辑交缠在一起。eg:网络请求失败或者数据库查询出问题都抛出HTTP的错误,而不是对应的业务错误在HTTP请求成功中返回。这就导致前端调试有时候莫名其妙,老是检查网络是不是内网,请求方式是否正确等等HTTP的检查。这点需要和后台做一个统一,统一之后的接口框架是HTTP错误就是HTTP错误,业务逻辑错误就是业务逻辑错误,二者不混在一起。另外对于接口返回的格式,数据部分也是需要特别规定的。一般来说前端验证了code有效之后,对于data中的数据默认是按照字典处理的,但有时候某个接口就只需要返回一个字段,data的类型可能就是个字符串。对于这种我觉得还是按照统一的字典比较好(后台返回个对象),无论是前端还是后台,强制解析类型不同的对象都会报错崩溃,所以约定为字典格式可以防止不必要的类型异常对后期的扩展也友好。

19.08.28

纯swift开发者似乎没有想象中的那么多

最近另外一个项目组在招纯swift开发,招了将近一个月,并没有找到合适的。来的很多面试者要么是混编要么swift并不是很熟悉(跟得上每次迭代的步伐),很多面试者依旧只是知道几个点,并没有系统地写过swift项目,唯一写过的几位依旧是按照OC的思想在翻译语言。swift出来也有些年头了,但似乎合格的swift开发还是非常少,能完全理解swift思想去用swift纯开发的就更少了,大多数人停留在混编、翻译语言、demo的阶段(我自己也是),说会吧,没有完全理解并运用到纯swift项目中,说不会吧,也能写能改,处在一种比较尴尬的境地。从这个月的招聘上看似乎并没有很多的合格者,但我觉得这正说明swift方向也是一个机遇,MacOS的需求目前也在增加,要求swift熟练会是一个趋势,所以swift的技能也要跟上步伐才行。

19.08.29

iOS13禁止KVC方式获取私有变量

iOS13禁止使用KVC的方式来获取对应的变量名,所以最近的一些APP更新了iOS13之后到处都在莫名其妙地崩溃。
这次的禁止确实是过于唐突,没有给足时间让开发者去fix KVC的地方,没有兼容线上的APP以至于线上的APP到处崩溃,美团、猫眼、淘宝等也无法幸免。个人觉得还不如从新提交审核的阶段入手,给线上的APP足够的兼容。

19.08.30

分类的前向引用替代KVC

最近两天也修复了一些使用KVC的地方,目前的工程中并没有大量使用KVC,只有小部分修改私有的一些属性和一些与swift桥接的关键字处理的地方。对于一些非KVC不可的地方(大部分也就是对私有变量的获取),如果无法暂时妥协,可以尝试使用分类的方式,在对应的分类中引入此变量,由于分类的前向引用,使得分类中变量的优先级更高。但是需要注意的是,这种方式一般只作为getter使用,即获取这个变量的值,setter方法也可以使用但是可能会出现一些难以预料的后果。

19.09.02

需要加密的信息放在后台处理

最近把项目中需要加密的地方统一做了更改,放在后台进行操作。例如支付宝SDK签名后的字符串,阿里OSS云服务登录的校验等等,我记得18年这些SDK已经提倡把加密措施放在后台保存。这两年的逆向越来越火也越来越成熟,虽然到最后可能无法成功签名逆向过的APP,但是APP内的一些内容多少还是能获取到,所以尽可能地把加密签名等操作放在后台是非常有必要的。

19.09.03

SDK方法兼容问题

最近搞了下环信,下的DEMO发现各种报错,看了下修改了不少的API,修改的地方不兼容之前老版本,新版本又没有及时地更新,导致很多地方报错,还要一点一点查看新的API去修改。
SDK更新不兼容老方法(直接舍弃老方法,新方法覆盖老方法)我觉得是非常不友好的方式,之前用过一家视频解决方案的SDK好像是叫云屋,新版本的SDK直接大量舍弃旧版本的方法,使得更新SDK之后大量的报错,加班加点去修改这些问题。苹果官方的方式则比较友好,新方法的出现并没有直接覆盖掉旧的方法,而是进行系统版本的限制兼容,这样即使更新也不会出现大量报错(swift3.0之前的问题就比较大,后续就好了很多)。
新旧方法的兼容往往是比较好的处理方式,覆盖这种方式一般是我们的SDK的某个方法必须让用户在更新了SDK之后做改变才会使用。

19.09.04

是否要做对输入框的实时限制

例如某个输入框只能输入中文汉字,是否要对其进行实时限制(输的不是中文则这次输入无效)?从产品角度来说是希望做好限制的,在输入的时候就希望用户能按要求输入正确的格式,但从开发角度来说,实时限制并不是这么容易操作,尤其是对于中文的限制。例如苹果自带的输入法在输中文时textfield里面其实已经是有拼音在出现了,虽然没形成最后的汉字,但此时代理方法是在执行的,而搜狗之类的第三方输入法,在其自身的输入法tool上进行拼音拼接,在textfield上显示最终的结果。五花八门的输入法会产生五花八门的问题,更别提复制粘贴之类的骚气操作。这就使得实时限制输入做起来非常麻烦,不是不能做,而是要用十倍的精力完成一倍的事情,有点得不偿失。
我参考了淘宝、支付宝、美团等APP的输入框,在登录注册时并没有限制用户的输入格式,甚至输入emoji也不会有提示,只在最后提交时做了一次校验。我觉得这种是比较合适的方式,虽然无法实时对用户的操作作出提示让其及时纠正,但保证了输入的连贯和校验的简单准确,避免了大量耗费精力的代码,算是折中的方式。

19.09.05

懒加载为相似度较高的页面美化代码

我们经常会遇到两个或多个相似的页面,这些页面可能仅仅是多了个按钮或者是多了几行显示或者是多了个图片等等,因为这些页面大部分是相同的,所以我们应该尽可能地去复用代码。在区分不用的页面时,不管是枚举也好,if...else...也好,总要涉及到新加入的控件的布局和事件处理,如果我们直接写,会使得代码臃肿可读性较差。这个时候懒加载就起到了它的作用。对于布局,我们可以用一根“引线”来穿起来控件,或者是利用公共部分的相对位置来处理,对于事件,则直接在懒加载中写好selector方法就OK。这样使得代码的层次比较分明,每个控件像插件一样随取随用,而不是臃肿地堆砌在一起。

19.09.06

APP未连接电脑启动崩溃的调试方式

这部分主要是在调试推送的部分出现的,本地定时推送在App杀死之后点击到来的推送直接崩溃。
这部分比较方便的调试是接入崩溃日志搜集的第三方,诸如bugly、友盟等等,在没有接入时如何调试这种状态呢?比较好的方式是收集数据写入本地。

例如当方法比较少的时候,我不确定启动的时候崩溃在哪个方法,我可以在每个方法前进行一次写入,第一个方法前写入

[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:@"1"];

依次往下,在连接调试启动时拿出这些值,看到哪个值中断。

也可以直接记录抛出的异常写入到本地,但有些时候异常可能捕捉不到或者比较抽象,具体不到我们对应的方法中,还是需要逐个方法去断开。

19.09.09

本地(延迟定时)推送对launchOptions的接收

测试系统版本为iOS12,以如下方式接收处理launchOptions启动直接崩溃:

NSDictionary *launchOptionsLoDicf = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

但同样的方式App杀死之后接收远程推送就OK:

NSDictionary *launchOptionsReDic = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

这里卡了一上午,收集了各个方面的资料,和网上的文章也做了一些对比和参考,并没有觉得有什么遗漏或者疏忽,所以我觉得可能是iOS12之后的问题,而网上的文章也没有更新,那么应该是权限或者数据问题。
在折腾了一会儿发现并没有什么权限之后,问题似乎是聚焦在了数据的身上,同事用了上述的方法帮我收集到了启动崩溃的问题,而这个崩溃收集到的也是曲折的,因为一开始并没有想到这个方法返回的是非基本类型,直到最后才想起转为字符串保证写入的问题。
问题如下:

[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]

这个方法返回的和远程推送返回的并不一样,远程推送返回的确确实实是一个字典,但这个方法中取值的Key返回了一个对象!而且这个问题似乎是iOS12之后才出现了,网上很多文章依旧是使用字典类型作为接收和取值,从而导致崩溃,但在iOS12以下的系统确是正常的。

所以正确的取值方式应该是如下:

UILocalNotification *launchOptionsLoNof = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (launchOptionsLoNof.userInfo) {
        
    }

这样才能把本地推送的消息处理到。

19.09.10

iOS13微信登录崩溃

先记录一下,暂未找到原因。
更新到最新的iOS13系统之后,微信登录成功后跳转回自己的App闪退,未捕获到任何异常。

19.09.11

使用发通知的方式销毁潜在的循环引用风险

项目里最近加了MQTT作为排队系统的支撑,个人觉得还是没有socket好操作,topic的方式可能更加偏向于硬件的简单低频通讯,在很多场景并不是socket的良好替代。

回到问题上来,因为排队设置超时,通话开始也设置超时,MQTT的订阅等等,所以这里很多个页面都涉及到计时器的引入,而且流程走下去页面深度有点大,在后续的页面我也要引用前面的一些时间数据,所以这个计时器就不能单单在页面消失的时候直接销毁。而MQTT的加入更是延迟了页面销毁的时间。

为了避免潜在的引用循环问题,保证中间流程的页面都能销毁掉,必须要找个时机把这些计时器之类的潜在隐患全部处理掉,分个处理不如一起处理来的方便,所以我的设计是在通话结束后,用户会返回到首页的时机发送一次通知,标明通话已经完成,该销毁的就销毁,中间各级页面中需要处理的潜在隐患全在这个时机进行处理,不再分开进行判断处理。

这样的好处是不需要在页面跳转处做很多销毁处理,集中统一时机进行处理使得代码逻辑简单,后期便于添加和维护。

19.09.12

present视图连弹的问题

需求是收到通知可能会连续弹出多个视图控制器。

A present B之后再次从A去presentC是没有效果的,也就是一个控制器只能弹出一次第二个控制器,无法多次弹出。目前我的解决方案是两种:

1.一种用最上层的控制器进行弹出。即A present B之后,B此时是最上层的,那么由B去present C。这种方式是最简单的,但是有个问题需要注意,B还在被present的过程中是无法present C的,此时present的动画并未完成,是无法进行下个操作的。所以这里可以去掉动画或者稍微延迟present;

2.弹出视图容器,只有容器本身是弹出的,多个弹出需求则是在容器中进行了addView的效果。这种方式的好处是可以自定义动画,舍去不必要的动画直接展示最上层的一个弹出内容。

19.09.16

分散监听键盘弹出处理高度的问题

如果A页面写了监听高度,B页面也写了监听高度,那么一旦A没有被释放,那么B页面的高度就会产生混乱。
这是在自测的时候发现的一个问题,虽然说这个问题是由于A没有被释放掉发生的,但是其实整体的设计来说也是有问题。对于这种全局通用的设计,应该选择集中处理,放在分类或者单例中激活一次即可,而不是应该分散在各个地方单独处理,类似于IQKeyboardManager的设计。
集中初始化,分散具体的实现,是一个好的设计思路。在日常的设计中,我们应当避免多次初始化同一件事物(这里的初始化并不单单指alloc,添加一次监听这种也算,主要指事物最开始的依赖);而对于需要特定的实现,例如键盘的高度,这部分才是需要预留接口的代码,把它们分布到每个具体实现中。

你可能感兴趣的:(每天梳理1~2个问题,坚持一下(四))