zoukankan      html  css  js  c++  java
  • 大年初一微信闪退?看看如何修复的

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

    本文由 微信终端开发团队团队发布在腾讯云+社区

    前言

    相信大家都遇到过一段特殊文本可以让iOS设备所有app闪退的经历。前段时间大年初一,又出现某个印度语字符引起iOS11系统奔溃。所幸微信客户端做了保护并没有引起太大问题。一般来说,特殊字符闪退是系统漏洞引起,只要更新系统就行。但大部分用户不愿意更新系统,而苹果也不一定第一时间解决问题。另外后台可以拦截恶意文本传递,但对于本地已下发的消息,后台没有办法让它删除。所以客户端还是要做些保护预防特殊字符闪退。

    方案

    由于无法事先知道字符串里包含特殊字符,所以只能先让它排版/绘制,看看是否出现问题。做法是,在排版/绘制字符串前,先设置标记位,排版/绘制结束后,移除标记位;一旦发现标记位存在,就意味着这字符串可能有问题,下次就不显示这个字符串:

    这里有几个问题:

    1. 有可能在排版/绘制过程中,其它线程crash,导致标记位不能正常移除。所以crash时要判断crash线程是否为排版/绘制线程。
    2. 究竟crash多少次才能判断这字符串是有问题的。最早做法是crash一次就直接屏蔽,但很多用户反馈,说某些好友昵称无法显示。其实iOS绘制字符串时也会极少概率出现闪退,从而误判。但crash两次才屏蔽的话,如果用户连续收到N条恶意消息,那么至少crash 2N次才彻底把所有有问题消息屏蔽。因此,第一次字符串crash先不屏蔽,后续连续字符串crash的话,直接屏蔽。这样crash N+1次就能处理完了。

    整个逻辑代码大致如下:

    // MessageItemView.mm, CP是CrashProtected的简称
    
    @implementation MessageItemView
    
    - (void)initContentLabel {
        m_label = [[MMCPLabel alloc] init];
        m_label.cpKey = [MMCPUtil generateKeyWithObject:self.messageModel];
        
        if ([MMCPUtil isUnsafeWithKey:m_label.cpKey]) {
            // 检测出messageModel消息内容有问题,屏蔽显示
            m_label.text = @"该内容无法显示";
        } else {
            m_label.text = self.messageModel.content;
        }
    }
    
    @end
    // MMCPLabel.mm
    
    @implementation MMCPLabel
    
    @synthesize cpKey = m_cpKey;
    
    // 对常用的排版/绘制接口做检查
    - (void)layoutSublayersOfLayer:(CALayer *)layer {
        CScopedCrashCounter crashCounter(m_cpKey);
        [super layoutSublayersOfLayer:layer];
    }
    
    - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
        CScopedCrashCounter crashCounter(m_cpKey);
        [super drawLayer:layer inContext:ctx];
    }
    
    - (CGSize)sizeThatFits:(CGSize)size {
        CScopedCrashCounter crashCounter(m_cpKey);
        return [super sizeThatFits:size];
    }
    
    @end
    // MMCPUtil.mm
    
    // 利用C++特性,在声明C++类临时变量时,会自动执行构造函数,离开作用域会执行析构函数
    // 因此构造函数做crashCount+1,析构函数做crashCount-1
    class CScopedCrashCounter {
    public:
        CScopedCrashCounter(NSString *cpKey) {
            m_cpKey = cpKey;
            [MMCPUtil increaseCrashCountWithKey:m_cpKey];
        }
        ~CScopedCrashCounter() {
            [MMCPUtil decreaseCrashCountWithKey:m_cpKey];
        }
    private:
        NSString *m_cpKey;
    };
    
    @implementation MMCPUtil
    
    @synthesize crashKeyMemoryMappedKV = m_crashKeyMemoryMappedKV; // 被判定为恶意信息对应的key
    @synthesize crashCountMemoryMappedKV = m_crashCountMemoryMappedKV; // 每个key crash次数
    
    - (BOOL)isUnsafeWithKey:(NSString *)key {
        return [m_crashKeyMemoryMappedKV getBoolForKey:key] == YES;
    }
    
    - (void)increaseCrashCountWithKey:(NSString *)key {
        // 这里记录key所在线程
        ...
    
        int32_t count = [m_crashCountMemoryMappedKV getInt32ForKey:key];
        [m_crashCountMemoryMappedKV setInt32:count + 1 forKey:key]
    }
    
    - (void)decreaseCrashCountWithKey:(NSString *)key {
        int32_t count = [m_crashCountMemoryMappedKV getInt32ForKey:key];
        [m_crashCountMemoryMappedKV setInt32:MAX(0, count - 1) forKey:key];
    }
    
    // crash回调函数
    - (void)onSignalCrash:(siginfo_t *)info {
        // 先找到跟crash线程相同的key
        NSString *key = [self lastCPKey:info->si_pid];
        if (key == nil) return;
        
        if (m_isLastTimeCrashedBySpecialCharacter == NO) {
            // 设置当前是特殊字符引起的闪退,如果crash次数大于1,则屏蔽这字符串显示
            [self setLastTimeCrashedBySpecialCharacter:YES];
            if ([m_crashCountMemoryMappedKV getInt32ForKey:key] > 1) {
                [m_crashKeyMemoryMappedKV setBool:YES forKey:key];
            }
        } else {
            // 连续特殊字符闪退,直接屏蔽
            [m_crashKeyMemoryMappedKV setBool:YES forKey:key];
        }
    }
    
    @end

    即使有了上面的N+1优化,当N很大时,客户端还是要crash很多次才能正常使用。之前有用户乱扫二维码被拉进炸群,如果不发红包,群主不停炸群;用户频繁crash,也无法退群。不少用户会选择卸载重装客户端。因此客户端要加上安全模式的机制。当客户端检测出连续三次crash,下次启动会出现安全模式的界面,提示用户如何处理:

    对于频繁闪退的群聊,主界面提供快捷入口方便用户退群。另外对于可能误判的字符串,界面也提供入口方便用户恢复字符串显示:

    为了让后台第一时间发现新的特殊字符变种,客户端检测出特殊字符crash后,会把相关信息上报到后台。通过客户端上报、后台拦截的闭环,能大大降低特殊字符传播范围。这方案不仅用于特殊字符,还能用于其他恶意信息,如炸群消息、GIF、小视频、链接等。

    MemoryMappedKV

    由于需要埋点的地方太多了,昵称、消息内容、头像等等,为了不影响滑动性能,guoling同学开发了一套基于mmap的高性能通用key-value存储组件,敬请留意WeMobileDev后续文章。

    问答

    短视频的发展前景以及未来发展方向?

    更多与短视频相关的精华问答,尽在腾讯云+社区!

    相关阅读

     

    谈谈编程

    程序员字典:「牛逼」

    码云推荐 | Symphony 社区平台的微信小程序

     

    此文已由作者授权腾讯云+社区发布,转载请注明文章出处

    原文链接:https://cloud.tencent.com/developer/article/1066209

  • 相关阅读:
    刷题总结——宠物收养所(bzoj1208)
    算法复习——trie树(poj2001)
    刷题总结——bzoj2243染色
    算法复习——虚树(消耗战bzoj2286)
    设置SSH自动登陆(免密码,用户名)
    自旋锁Spin lock与互斥锁Mutex的区别
    如何去除Linux文件的^M字符
    重构
    比赛日程安排
    基于libzip的简易压缩(zip)/解压缩(unzip)程序
  • 原文地址:https://www.cnblogs.com/qcloud1001/p/8669970.html
Copyright © 2011-2022 走看看