iOS版微信是如何防止特殊字符導(dǎo)致的炸群、APP崩潰的?

1、引言

相信大家都遇到過(guò)一段特殊文本可以讓iOS設(shè)備所有app閃退的經(jīng)歷。前段時(shí)間大年初一,又出現(xiàn)某個(gè)印度語(yǔ)字符引起iOS11系統(tǒng)奔潰,所幸iOS版微信客戶端做了保護(hù)并沒(méi)有引起太大問(wèn)題(字符處理這類技術(shù)問(wèn)題,其實(shí)曾在Android版微信上導(dǎo)致過(guò)嚴(yán)重的用戶體驗(yàn)危機(jī)。

一般來(lái)說(shuō),特殊字符閃退是系統(tǒng)漏洞引起,只要更新系統(tǒng)就行。但大部分用戶不愿意更新系統(tǒng),而蘋(píng)果也不一定第一時(shí)間解決問(wèn)題。另外后臺(tái)可以攔截惡意文本傳遞,但對(duì)于本地已下發(fā)的消息,后臺(tái)沒(méi)有辦法讓它刪除。所以客戶端還是要做些保護(hù)預(yù)防特殊字符閃退。

2、微信的思路

由于無(wú)法事先知道字符串里包含特殊字符,所以只能先讓它排版/繪制,看看是否出現(xiàn)問(wèn)題。做法是,在排版/繪制字符串前,先設(shè)置標(biāo)記位,排版/繪制結(jié)束后,移除標(biāo)記位。

一旦發(fā)現(xiàn)標(biāo)記位存在,就意味著這字符串可能有問(wèn)題,下次就不顯示這個(gè)字符串:

這里有幾個(gè)問(wèn)題:

有可能在排版/繪制過(guò)程中,其它線程crash,導(dǎo)致標(biāo)記位不能正常移除。所以crash時(shí)要判斷crash線程是否為排版/繪制線程。

究竟crash多少次才能判斷這字符串是有問(wèn)題的:最早做法是crash一次就直接屏蔽,但很多用戶反饋,說(shuō)某些好友昵稱無(wú)法顯示。其實(shí)iOS繪制字符串時(shí)也會(huì)極少概率出現(xiàn)閃退,從而誤判。但crash兩次才屏蔽的話,如果用戶連續(xù)收到N條惡意消息,那么至少crash 2N次才徹底把所有有問(wèn)題消息屏蔽。

因此,第一次字符串crash先不屏蔽,后續(xù)連續(xù)字符串crash的話,直接屏蔽。這樣crash N+1次就能處理完了。

3、具體的iOS代碼實(shí)現(xiàn)

正如第2節(jié)的思路那樣。整個(gè)邏輯代碼大致如下。

MessageItemView.mm:

//CP是CrashProtected的簡(jiǎn)稱

@implementationMessageItemView

- (void)initContentLabel {

m_label = [[MMCPLabel alloc] init];

m_label.cpKey = [MMCPUtil generateKeyWithObject:self.messageModel];

if([MMCPUtil isUnsafeWithKey:m_label.cpKey]) {

// 檢測(cè)出messageModel消息內(nèi)容有問(wèn)題,屏蔽顯示

m_label.text = @"該內(nèi)容無(wú)法顯示";

} else{

m_label.text = self.messageModel.content;

}

}

@end

MMCPLabel.mm:

@implementationMMCPLabel

@synthesizecpKey = m_cpKey;

// 對(duì)常用的排版/繪制接口做檢查

- (void)layoutSublayersOfLayer:(CALayer *)layer {

CScopedCrashCounter crashCounter(m_cpKey);

[superlayoutSublayersOfLayer:layer];

}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

CScopedCrashCounter crashCounter(m_cpKey);

[superdrawLayer:layer inContext:ctx];

}

- (CGSize)sizeThatFits:(CGSize)size {

CScopedCrashCounter crashCounter(m_cpKey);

return[supersizeThatFits:size];

}

@end

MMCPUtil.mm:

// 利用C++特性,在聲明C++類臨時(shí)變量時(shí),會(huì)自動(dòng)執(zhí)行構(gòu)造函數(shù),離開(kāi)作用域會(huì)執(zhí)行析構(gòu)函數(shù)

// 因此構(gòu)造函數(shù)做crashCount+1,析構(gòu)函數(shù)做crashCount-1

classCScopedCrashCounter {

public:

CScopedCrashCounter(NSString*cpKey) {

m_cpKey = cpKey;

[MMCPUtil increaseCrashCountWithKey:m_cpKey];

}

~CScopedCrashCounter() {

[MMCPUtil decreaseCrashCountWithKey:m_cpKey];

}

private:

NSString*m_cpKey;

};

@implementationMMCPUtil

@synthesizecrashKeyMemoryMappedKV = m_crashKeyMemoryMappedKV; // 被判定為惡意信息對(duì)應(yīng)的key

@synthesizecrashCountMemoryMappedKV = m_crashCountMemoryMappedKV; // 每個(gè)key crash次數(shù)

- (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回調(diào)函數(shù)

- (void)onSignalCrash:(siginfo_t *)info {

// 先找到跟crash線程相同的key

NSString*key = [selflastCPKey:info->si_pid];

if(key == nil) return;

if(m_isLastTimeCrashedBySpecialCharacter == NO) {

// 設(shè)置當(dāng)前是特殊字符引起的閃退,如果crash次數(shù)大于1,則屏蔽這字符串顯示

[selfsetLastTimeCrashedBySpecialCharacter:YES];

if([m_crashCountMemoryMappedKV getInt32ForKey:key] > 1) {

[m_crashKeyMemoryMappedKV setBool:YESforKey:key];

}

} else{

// 連續(xù)特殊字符閃退,直接屏蔽

[m_crashKeyMemoryMappedKV setBool:YESforKey:key];

}

}

@end

4、還需要從用戶體驗(yàn)上做更進(jìn)一步改進(jìn)

即使有了上面的N+1優(yōu)化,當(dāng)N很大時(shí),客戶端還是要crash很多次才能正常使用。之前有用戶亂掃二維碼被拉進(jìn)炸群,如果不發(fā)紅包,群主不停炸群;用戶頻繁crash,也無(wú)法退群。不少用戶會(huì)選擇卸載重裝客戶端。因此客戶端要加上安全模式的機(jī)制。

當(dāng)客戶端檢測(cè)出連續(xù)三次crash,下次啟動(dòng)會(huì)出現(xiàn)安全模式的界面,提示用戶如何處理:

對(duì)于頻繁閃退的群聊,主界面提供快捷入口方便用戶退群。另外對(duì)于可能誤判的字符串,界面也提供入口方便用戶恢復(fù)字符串顯示:

為了讓后臺(tái)第一時(shí)間發(fā)現(xiàn)新的特殊字符變種,客戶端檢測(cè)出特殊字符crash后,會(huì)把相關(guān)信息上報(bào)到后臺(tái)。通過(guò)客戶端上報(bào)、后臺(tái)攔截的閉環(huán),能大大降低特殊字符傳播范圍。這方案不僅用于特殊字符,還能用于其他惡意信息,如炸群消息、GIF、小視頻、鏈接等。

?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 最先接觸編程的知識(shí)是在大學(xué)里面,大學(xué)里面學(xué)了一些基礎(chǔ)的知識(shí),c語(yǔ)言,java語(yǔ)言,單片機(jī)的匯編語(yǔ)言等;大學(xué)畢...
    oceanfive閱讀 3,367評(píng)論 0 7
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,579評(píng)論 30 472
  • 轉(zhuǎn)自http://www.raywenderlich.com/zh-hans/30818/ios應(yīng)用崩潰日志揭秘 ...
    RunSnails閱讀 4,635評(píng)論 2 22
  • 閔杏郁,溫州圣卡蘿服飾有限公司;63期同修、79期志工、116期志工、160期志工、203期志工、224期志工【日...
    杏郁閱讀 272評(píng)論 0 0
  • 憑空不見(jiàn)愁緒滿天,花開(kāi)自落佛門(mén)無(wú)過(guò) 安然問(wèn)己何處歸去?躊渡三生河苦無(wú)文三錢(qián) 待情郎,望我可憐處給予艄公今生舵 默默...
    誰(shuí)家璧人閱讀 248評(píng)論 12 0

友情鏈接更多精彩內(nèi)容