博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
大年初一微信闪退?看看如何修复的
阅读量:6321 次
发布时间:2019-06-22

本文共 3707 字,大约阅读时间需要 12 分钟。

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

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

前言

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

方案

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

img

这里有几个问题:

  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-1class 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,下次启动会出现安全模式的界面,提示用户如何处理:

img

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

img

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

MemoryMappedKV

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

问答

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

相关阅读

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

原文链接:

img

你可能感兴趣的文章
BZOJ 2118 Dijkstra
查看>>
Jenkins
查看>>
linux下使用screen和ping命令对网络质量进行监控
查看>>
segment
查看>>
面试/编程
查看>>
打造一个上传图片到图床利器的插件(Mac版 开源)
查看>>
thinkphp判断更新是否成功
查看>>
高效使用jquery之一:请使用'On'函数
查看>>
人脸识别 开放书籍 下载地址
查看>>
AspNetPager控件的最基本用法
查看>>
sessionKey
查看>>
iOS8 Push Notifications
查看>>
各大名企笔试及面经大全(程序猿必读)
查看>>
轨磁条简介
查看>>
如何设计高扩展的在线网页制作平台
查看>>
Git 2.5增加了工作树、改进了三角工作流、性能等诸多方面
查看>>
云服务正在吞噬世界!
查看>>
高性能 Lua 技巧(译)
查看>>
区分指针、变量名、指针所指向的内存
查看>>
最近话题火爆的四件事你知道不?
查看>>