罗列下这几年开发iOS的一些坑和个人的一些技巧总结。全是回忆所得,并不完整。
1.属性修饰符误写,例如float写了strong等,容易出现手误。其实这个可以用脚本检查。
2.WP本地js跨域问题。这不是iOS问题,是WP问题,最好是把网页做成远端的,当初采用本地原生请求,传数据到js处理。这个问题我一直记着,当初就有跨域的猜测,直到后来对web开发有了更多的了解之后,才肯定了这点。
3.系统联系人选择器dismiss问题。处理办法:初期作为tabbarVC的一个tab来避免,后期因内置行为不符合我们的需要,直接拨打了,没法自定义点击后的行为,于是采取自定义联系人选择器。
4.七牛短视频切片播放不完全。处理办法:未处理,可以考虑不切片,暂时不清楚是否是切片导致。
5.七牛视频上传方向问题。处理办法:未处理,可以考虑上传cdn前进行校正,或者存储视频方向等办法。
6.block内存泄漏问题。处理办法:MLeakers发现并修正,人肉识别,提升对block的认识之后,可以避免这些问题,例如UIVIew动画一般不会导致循环引用,类似的类方法一般不会导致循环引用。
7.个推UserNotification因为强连接导致低版本iOS系统上崩溃问题。处理办法:使用弱连接。这是我第一次听说这种framework的链接方式,原来要区分强链接和弱链接。
8.推送没有处理前后台判断,导致前台直接跳页面。处理办法:判断了收到通知时的前后台状态。刚做推送,没料到应用在前台也会收到静默的推送消息。
9.json model特性,字段多于实际接收会解析失败。处理办法:线上全部可选,开发限制。刚接触json转model,以前都是XML。经常遇到缺字段,导致页面变空白了,线下还好,线上就麻烦了。后来为了扩大容错性,BaseModel里根据debug编译标识返回全部可选或不可选,子类可以按需覆盖。
10.配置了过多图片,导致内存暴涨崩溃。处理办法:跳过了SDWebImage的转码功能。
11.手机内存及其紧张时,二维码扫描库Zxing及其封装都会发生崩溃。感觉内存不足会影响自动释放的性能。
12.多个App使用相同的scheme导致冲突。处理办法:后续将scheme配置在info文件里了。这个需要配合我们的URL组件化机制。
13.网络鉴权问题,处理办法:通过版本检测先触发网络鉴权,引导图消失前发个通知让首页重新加载。
14.BlocksKit库多次bk_whenTapped导致卡顿。处理办法:只在cell的init里或者View的懒加载的初始化中调用。
15.某个页面设置了UITextField、UITextView的inputAccessoryView,如果重置会导致inputAccessoryView出现在别的页面,而inputAccessoryView里添加的UIBarButtonItem的target和action是另一个页面的,可能会导致崩溃或者看起来无响应。处理办法:后来使用IQKeyboardManager管理键盘。
16.微信分享预览图分辨率限制问题,图片过大导致无法唤起分享。处理办法:通过重绘限制分辨率和转换成JPEG,不断尝试压缩比例。
17.下拉刷新清空数组,请求完成后数据加到数据在刷新这段时间内,滚动tableview就会发生数组越界问题。处理办法:使用安全的数组方法和将数组清空放到请求完成才进行。
18.某些页面会触发登陆,跳到登录页面,这时候用户不点登陆返回就会陷入循环中。处理办法:触发登陆后pop到根视图控制器,选择不会触发登陆的首页tab,然后push登录页面进去。会打破正常交互流程,但不会陷入循环中。
19.引导图过大长期驻留内存。处理办法:不放在assets里,不使用imageNamed。
20.iOS9以前注册通知中心不移除,对象dealloc后发生崩溃。KVO也有类似问题。
21.NSTimer强引用导致的循环引用问题,自己捕获自己引起的循环引用问题等。
22.注册通知的时机问题。有些通知可以推迟到didLoadView注册,有些init的时候就得注册。例如tabbar item是可配置的,那么请求得到配置后,就得发通知让TabBar VC的所有children自行改变item,这时候由于一些页面并未显示出来,didLoadView就不会被调用,在这里的注册的通知不会被观察到。
23.标记代替调用。我们从一个页面push到另一个页面后,在新的页面进行操作可能会对前面一个页面产生影响,需要刷新前面一个页面。但是每次做修改,就刷新前面一个页面又没有必要,因为页面被遮挡了。此时可以发送通知让前面一个页面标记为再次展示时需要刷新。当pop到那个页面时,那个页面的viewWillAppear才去检查刷新标志位,判断是否需要刷新页面。
24.performSelector系列不能传基本类型数据。如果你想通过performSelector为一个属性设置BOOL型NO是不能实现的。因为@(NO)作为bool参数的实际值时是对象地址,必然为YES。
25.某段时间不知道是服务器抽风还是怎么的,加载页面很慢,导致审核被拒,说页面空白。后来给加载图片的地方都加了灰色的占位图,不是纯色的话,可能会变形。由于用的SDWebImage,只需要在最高层的接口写死默认的占位图就可以了。不同的业务需要被不同占位图时,采用较低一级的接口指定占位图即可。
26.对于请求的设计。
请求除了API要求的业务参数外,应该由框架自动添加全局参数,全局参数包括版本号、客户端类型、sign等。版本号、客户端类型用于版本检测、兼容性处理、统计等目的。sign则是作为登陆用户的唯一标识。这些参数不仅要带入接口,也要带入webview组件中。
响应一般包括状态码,消息短语,响应内容实体组成。参考结构如下:
```
{
status:{
code:1001,
message:"OK"
},
result:{}
}
```
我们需要对status进行分类,哪些是正常的,那些是异常的。正常我们一般就1001,message可以是正确反映接口行为的,也可以是笼统的OK。前者的话,后端压力较大,需要指定不同的提示。后者前端需要灵活展示,又要考虑各端的一致性问题。对于异常类的code,那么只能后端提供错误提示了,因为前端对此并不知晓。在一般开发中,可以定义若干个固定的异常,例如要触发跳转登陆页面的(这种情况适合必须要登录才能完成余下操作的,例如商品页面到确认订单页面),要提示未登录的(这种情况适合业务上要求页面需要根据登陆与否展示不同的样子),要返回上个页面的(这种情况比较罕见,例如你在商品页面缺乏检查接口,跳到确认订单页面时才发现商品不可购买,这时候最好是提示后返回上个页面)等。固定的code适合所有的业务,属于保留code。每个业务又可以根据自己的特点增加code,属于自定义code。对于保留的code,都是框架内部处理即可,业务层不需要关心它们,只需要展示消息即可。框架内部可以提供一些block来全局处理固定的code。但是我们实践中并未遇到这种需求。
27.cell的高度存储。面对复杂的cell,我们一般为提高性能,会将cell的高度存起来,避免多次计算,这是一种空间换取时间的策略。每个cell的高度我们可以考虑存在VC的mutable dictionary中,以indexPath为键,高度为值。这里有些问题,一每个页面都要提供这个可变字典,并提供读写设施。二不适合列表数据会变动的情况,例如列表中的项目可能会被删除、修改等。使用id代替indexPath可以解决项目被删除的问题。但是项目在本地被修改需要更新高度,在远端被修改需要刷新列表数据同时重建可变字典。还有一种思路是将高度存在模型中,这样就不需要可变字典和读写设施了,但是每次刷新数据,高度都需要重新计算,这种刷新是符合一般需求的。唯一的缺陷是不适合单个模型多个视图的情况,但是这种情况很罕见,例如左侧有一个联系人列表,右侧有个选中的联系人列表,它们的模型相同,但是cell是两种cell,这时候模型想存储多种高度就不可行了。这种模型很容易实现,只需要在基类增加__height或xxHeight等不会被业务使用到的字段就行,并不会增加太多负担和侵入,但是要为模型中的字段增加高度支持就会污染模型,因为它们需要在业务模型中分别增加,还会和业务字段混在一起。顺便说道,这种为业务模型增加本地字段,提供额外的功能支持,还被用在列表的选择(本地增加xxSelected字段)等场景。
28.请求基类的利用。我们的请求按功能是可以划分为基础请求和业务请求的。基础请求往往是那些上传图片、文件、获取省市区信息等的接口,它们往往可以分成多个请求,分别归属到FileRequest和LocationRequest下,也可以一股脑儿放到UtilsRequest下。但是归属到BaseRequest下的话,可以在任意请求中使用它们,而无需导入业务请求之外的头文件,也就给编码带来一些便利。
29.Xcode9.2打包App在iOS8.1-iOS8.3上图片花屏(https://www.jianshu.com/p/5f0f2004bd64)当时是回退到Xcode9.1打的包。没办法,不可能不用assets。
30.这个跟业务有点关系,以前项目中用到了趣拍SDK,通过method swizzling改变了一些功能,例如拍摄分辨率,授权验证等。发现它没有实现imagepicker的一个代理,导致不能dismiss,就用class_add给它加了个imagePickerControllerDidCancel的实现。发现会在iOS7上崩溃,最后定位到原因是使用了iOS8上才引入的NSURLQueryItem,就替换了它的实现。这里主要是说,掌握runtime的一些技巧,有助于解决一些本来要央求他人的问题,显然我们不能等待人家修复这个问题才上线。
31.用手写一堆视图的属性是很繁琐的事情,因此根据我们的框架和我的编码习惯,我开发了根据属性一键生成属性代码的Xcode插件,生成完之后,只需要根据设计稿去设置字体、颜色、frame就行,大大减轻了敲代码的负担,也统一了编码风格。这个插件的github地址:
https://github.com/Mamong/XMCodeGenerator。稍作修改应该就可以适应不同的项目中。