iOSAPP优化-细节

·  阅读 1682

认真的匠人.jpeg

这里盗用一张图[手动狗头],其实我觉得我们做程序开发者一样也要有一种匠人精神,把产品作为自己的一件工艺品,不光要实现产品功能,而且还要做到好的优化。

因为最近刚好看到"高性能iOS应用开发"一书,跟看了戴铭的"iOS开发高手课"的内容,(这里不是推销的呀),受到了些许的感悟,所以决定也尝试着对我们的app也做一些优化,并把优化的过程记录下来跟大家一起分享,有不对的还希望大家指正。我准备通过开发中要注意的细节、复杂UI布局的探索、包内容的大小、启动时间、网络及耗电量这几个维度来入手。这里就先讲一下平时开发中应该注意的细节。

把细节放到第一篇来讲是因为我觉得这些是从项目的一开始我们时时刻刻都会遇到的内容,也容易被我们忽略的内容,虽然平时也会想到,但是有时候也是因为懒,就不愿意去做,虽然每一块也对app的性能影响都不打,但是积少成多。所以我觉得细节很重要。下面我就详细列举一下,关联性不是很强,都是比较零散的点。

  1. 代理使用 对于调用比较频率的代理方法来说,我们调用前都会通过[_delegate respondsToSelector:@selector(func)]此方法来判断这个代理对象能否响应相关选择子。可是如果频繁调用此方法,除了第一次有用,后续的检测可能是多余的。一般不太会突然无法响应某个原来可以响应的选择子。对此我们可以把某个代理对象能否响应某一代理方法这个信息缓存起来。

以项目中的ServerCenter为例。


@interface CLCClassPageServerCenter () {
    struct {
        unsigned int requestSuccess :1;
        unsigned int requestFailed  :1;
    }_delegateFlags;
}
@end

- (void)setDelegate:(id<ServerCenterResponseDelegate>)delegate {
    _delegate = delegate;
    _delegateFlags.requestSuccess = [delegate respondsToSelector:@selector(requestServerCenterSuccessWithType:serverCenter:)];
    _delegateFlags.requestFailed = [delegate respondsToSelector:@selector(requestServerCenterFailed:type:serverCenter:)];
}

- (void)requestServerCenterFinished:(BOOL)succeeded type:(NSInteger)type userInfo:(NSDictionary *)userInfo {
    if (succeeded) {
        if (_delegateFlags.requestSuccess) {
            [self.delegate requestServerCenterSuccessWithType:type serverCenter:self];
        }
    } else {
        if (_delegateFlags.requestFailed) {
            [self.delegate requestServerCenterFailed:userInfo type:type serverCenter:self];
        }
    }
}

复制代码

可以用个结构体的flag标识来标识某个代理方法是否实现,这里建议使用“位段”数据类型,这个每个flag标识只需要1个二进制位,比较省内存。

  1. 函数传参 因为arm64架构下有8个参数寄存器,参数超过8个的话就会入栈,影响效率,所以c函数参数最好不要超过8个,OC函数参数最好不要超过6个(OC方法默认有两个隐藏参数self跟SEL),如果参数过多可以考虑用字典或者一个配置文件configure来传参。

  2. 在对象内部尽量直接访问实例变量 因为这样不用经过“方法派发”,所以直接访问实例变量会比较快,编译器所生成的代码会直接访问保存对象实例的那块内存。 但是对于需要“键值观察”或者懒加载的属性需要使用“点语法”。 有一种折中的方法就是写入实例变量时,通过其“设置方法”来做,而读取实例变量时则直接访问。但是switch...case只能处理case为常量的情况,对非常量的情况是无能为力的。例如 if (a > 1 && a < 100),是无法使用switch...case来处理的。所以,switch只能是在常量选择分支时比ifelse效率高,但是ifelse能应用于更多的场合,ifelse比较灵活。

  3. switch、 if else的使用 首先是尽可能减少if else的嵌套层级,如果判断条件大于3个的时候尽量使用switch,因为生成汇编的时候switch...case会生成一个跳转表来指示实际的case分支的地址,所以switch执行效率会更高。但是case条件要连续,如果是1、20、40跨度比较大的话最终生成大汇编还是if else。

  4. 耗时操作尽量放在异步,主线程以UI的展示优先。

  5. +(void)load方法的使用 我们平时会做一些hook操作,一般会放在+(void)load方法里面,但是因为项目大致加载流程 1装载  APP的可执行文件.o,同时会递归 加载所有依赖的动态库dyly -> 调用所有Class和Category的+load方法(加载内存中) -> 3dyld就会调用main函数,+(void)load方操作过多会影响app启动的速度。建议放到+(void)initialize方法里面,此方法在类收到第一条mesgSend消息的时候会调用,所以放到此方法里面能起到相同的作用(需要注意父类、子类、分类的调用顺序及覆盖)。但是也不建议做太多操作。

  6. tableView、collectionview刷新的时候尽量少的刷新,可以刷新某一行或某一组,按需刷新

  7. 项目中发现某些页面刷新后原来已经有的页面会被异常页面覆盖,建议在强求数据的时候做判断,对于原来已经有页面的数据在网络请求失败后不展示异常页面。增加用户体验友好性。

  8. 避免对象的频繁销毁与创建,对于需要大量销毁的地方可以借用NSAutoreleasePool自动释放池。避免内存峰值过大。

  9. 引用头文件的时机尽量延后,尽量在.m文件里面遵守协议

  10. static、const、extern、define 合理使用,对于在当前文件内的使用的常量就用static const组合,对于多文件共用的常量就用extern const组合。 尽量少的用预处理指令(宏”define“)定义常量。因为假设某宏定义声明在某个头文件中,那么所有引入了这个头文件的代码,其中的这个宏定义的地方都会被替换。同时定义很多的宏在项目中编译的时间也会增加。而且这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。

  11. 谨慎使用atomic atomic下会在getter/setter方法时加锁,但是@synchronized是互斥锁,会比较消耗性能。

  12. viewMode中暴露给外部的属性应该是只读的,建议属性.h文件中用readonly修饰,.m文件中用readwrite修饰。

  13. 避免过多的线程切换及控制线程的数量。

  14. 对于循环遍历建议使用枚举。

  15. 对于UI的一些使用建议

  • 对于frame固定的控件,懒加载的时候就初始化好frame,不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改,尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性。
  • 尽量减少页面视图的层级,因为UI层级过多首先会多占用内存,而且造成了事件响应链传递的过程,影响事件响应的时间。
  • UI控件尽量使用轻量级的,例如没有响应事件的控件,可以考虑使用layer代替。
  • 图片的size最好刚好跟UIImageView的size保持一致。
  • 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示。
  • 减少透明的视图(alpha<1),不透明的就设置opaque为YES
  • 尽量避免使用layder添加圆角、加边线、加阴影,边线跟圆角可以使用UIBezierPath绘制,阴影可以让UI切张小的图,然后设置拉伸。 *尽量不要使用ClearColor,无背景色,透明度也不要设置为0 *提前计算并缓存cell的属性及内容,缓存cell的高度
  1. 大家使用蓝湖下载图片的时候建议打开图片压缩功能,画质不影响展示的话尽量选择低画质。 切图.png

  2. 对于一些第三方的应用,可能我们只用到了其中一部分功能,建议重写,选择出我们用到的,减少包的大小跟代码量。

  3. 对于列表页面的请求可以根据网络的情况来设置请求数据pageSize的大小。

  4. 尽量使用类方法。从isa流程图上来看,类方法会比实例方法少查找一次,所以效率会更高。 isa流程图.png

  5. 对方三方库的一些使用,最好封装一层,可以方便以后的替换。

  6. 定义模型的时候尽量只添加用到的属性,因为成员变量会影响对象的大小,且属性过多也会影响转模型的效率。

细节方法暂时想到的就这么多,大家有好的建议可以补充,上面有不对的也欢迎指出。

持续更新改正中......

分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改