iOS IM启动逻辑梳理及优化

967 阅读4分钟

一、app启动原理

1.app启动分为冷启动和热启动。

App 的启动主要包括三个阶段:main() 函数执行前;main() 函数执行后;首屏渲染完成后。

1. main() 函数执行前;

加载Mach-o文件;加载动态库;Objc类、分类、方法初始化;+load()方法初始化。

可以优化的点:减少动态库加载,即合并动态库;采用懒加载的方式调用类或方法;+load()使用+initialize()方法替换;控制C++全局变量数量。

2. main() 函数执行后

从main()函数到didFinishLaunchingWithOptions方法里首屏渲染完成。 一般app的配置信息,初始化,信息上报等都在这里。放在了首屏渲染之前。

可以优化的点:按功能梳理出首屏必要的初始化功能。

3. 首屏渲染完成后

主要完成的是,非首屏其他业务服务模块的初始化、监听的注册、配置文件的读取等 问题:什么样的功能适合放在首屏渲染后呢?应该先检测方法耗时,按耗时严重的去重点关注。

app启动速度的监控 第一种:定时抓取主线程的方法调用栈,计算一段时间里的各个方法耗时。 第二种:对objc_msgSend方法进行hook来掌握所有的方法执行耗时。

自定义方法耗时工具 fishhook:在 iOS 上运行的 Mach-O 二进制文件中动态地重新绑定符号

二、启动时加载逻辑梳理

1. 通讯之前有哪些逻辑?

  1. 请求LBS接口(获取socket连接地址)
  2. 拿到IP、端口后socket连接

2. 一个正常的即时通讯软件,启动时需要哪些逻辑。

  1. 获取服务端时间
  2. 同步拉取个人信息、名片信息、同步个人设置
  3. 同步好友信息、同步最近联系人
  4. 同步组织信息
  5. 同步群组资料信息
  6. 同步群成员列表及禁言信息

思考及优化

  1. LBS接口的作用 LBS仅返回一个地址,首先必须返回是IP地址,避免DNS解析耗时。 LBS接口还有一个作用,就是负载均衡,服务端有多台服务器,但那台服务器处于空闲,由服务端返回最优的IP.

  2. 第二步的逻辑有些多 接口能否合并,如个人信息与个人设置 是否所有消息都是必须拉取,如群成员列表是否进入会话才关注 已经加载过的,考虑增量拉取

三、启动时消息加载慢问题排查

收到反馈:一条信息从启动或退到后台后再进入app,新消息展示大概6、7s。 一个IM软件,这肯定是不能容忍的。

新消息展示流程

  1. 一条新信息的路程:客户端进入app,请求lbs接口,进行socket重连,主动拉取好友信息、个人信息及其他信息;服务端会进行消息推送。
  2. 消息到达本地渲染会话或聊天消息。

思考

问题偶现,线上问题。如何排查是个问题。

加日志,打印时间。如果总的时间超过5s,判断为异常情况。可以上报耗时情况。 服务端从socket连接上、到消息推送耗时加日志。 两条腿走路,确认耗时原因。

解决

  1. 发现最耗时的时间段:在socket连接成功后到消息推到客户端占了5s多。
  2. 服务端逻辑问题:先查询所有会话,再去过滤未读的会话推送给客户端。当会话量大时,耗时严重。
  3. 服务端直接查询未读的会话,再做其他处理。限制在400ms。

四、启动拉取个人信息偶现闪退

##1.dispatch_group_t ###场景:一般在并发多个网络请求都返回时,处理逻辑会用到。 dispatch_group_enter与dispatch_group_leave需要成对出现。但dispatch_group_leave出现比dispatch_group_enter多时会崩溃。 例如在网络异常时,出现超时重试多次回调了block。这种场景不是必现,也不好复现,bugly监控只能看到在某个函数崩溃。我认为直接使用系统提供的是不安全的。 group使用崩溃.png

我的解决方案:

  1. 封装dispatch_group_t使用,当leave出现异常时使用NSAssert处理
@interface BLGCDGroupManager()
{
    dispatch_group_t _group;
    dispatch_queue_t _queue;
    NSInteger _count;
    NSLock * _lock;
}
@end

@implementation BLGCDGroupManager

- (instancetype)initWithGroup:(dispatch_group_t)group
                        queue:(dispatch_queue_t)queue{
    self = [super init];
    if (self) {
        _group = group;
        _queue = queue;
        _lock = [[NSLock alloc] init];
    }
    return self;
}

- (void)enter{
    if (_group) {
        [self add];
        dispatch_group_enter(_group);
    }
}

- (void)leave{
    if (_group && _count > 0) {
        [self sub];
        dispatch_group_leave(_group);
    }else{
        NSAssert(@"leave使用出现异常", nil);
    }
}

- (void)add{
    [_lock lock];
    _count ++;
    [_lock unlock];
}

- (void)sub{
    [_lock lock];
    _count --;
    [_lock unlock];
}

@end
  1. 业务上的处理 找出dispatch_group_leave为什么会出现两次。