日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

【iOS】Cocoa(iOS,OSX)安保系统设计实现

發(fā)布時間:2024/1/18 windows 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【iOS】Cocoa(iOS,OSX)安保系统设计实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

這里主要以iOS和OSX講講crash閃退怎么防御。
其中最新的OSX應(yīng)用本身就有一定閃退防御,但有點類似@try @catch在最外層包了一下普通的越界調(diào)用空方法都會中斷在操作位置不向下執(zhí)行,如果沒有進一步復(fù)雜邏輯不會閃退,只是影響后續(xù)的操作。

而iOS則沒這么好說話了,二話不說直接閃退給你看沒有上面的那種機制。

所以才有了設(shè)計一個安保系統(tǒng)的意義,來保證最大程度的健壯性,理想的狀態(tài)就是不crash且能繼續(xù)正常運行后面的邏輯。

參考了眾多網(wǎng)上的資料有了下面的小成果分享出來,這其實只是安保系統(tǒng)最后的一個環(huán)節(jié)的防御

安保系統(tǒng)設(shè)計

這里我所認為的安保系統(tǒng)應(yīng)該從代碼和規(guī)范兩個層面看,畢竟想抓到所有的crash情況是一定不可能的,現(xiàn)實中即使處處try catch都沒法保證抓到所有crash!

代碼

  • swizzing切面

  • 方法防御選型

  • 防御成功上報

程序內(nèi)需要的是代碼,這個模塊是要沒有任何侵入性的,所以切面是必須的,其次就是盡量的細化切面顆粒度保證意外情況最小化!

另一點就是切面以后我們對原方法應(yīng)該采取怎樣的防御,這里即可以try catch的形式也可以進行邏輯判斷形式。
而我的代碼里用邏輯判斷,更多的考量是針對的函數(shù)都偏下層且容易使用時外部恰巧又有各種循環(huán)邏輯,那樣相較之下try catch在不間斷的調(diào)用性能會有一定影響,所以暫時沒用try catch作為防御的手段。
從另一角度看其實try catch的使用場景有些方法還是比較合適的,首先我們在防御時方法顆粒度已經(jīng)很細所以抓住異常都會做對應(yīng)處理不會有內(nèi)存泄漏或邏輯遺漏,另外無論try還是catch內(nèi)的方法也不會太多,滿足了`try catch的最佳場景,只是個別方法循環(huán)利用略過高可能性能沒法到達極致僅此而已。

防御完了crash就是上報,我們保護了程序的同時也就意味著有地方寫的有問題,由于沒crash所以沒crash log,這時候就需要在安保模塊里加入上報機制,這時候我的做法則是放出一個協(xié)議等人去實現(xiàn),安保模塊就專心處理防御的事情,上報到服務(wù)端的事情交給專門處理這事的模塊,我們只需要在防御成功時告知協(xié)議有這么個事情即可。剩下的就是個人看情況如需詳細情況直接[NSThread callStackSymbols]把棧信息輸出一下!

1 2 3 4 5 6 7 8 9 10 //安保模塊上報協(xié)議 @protocol?SafeObjectReportProtocol @required /** ? 上報防御的crash?log ? ? ? @param?log?log無法抓到Notification的遺漏注銷情況 ? */ -( void )reportDefendCrashLog:(NSString*)log; @end

而實現(xiàn)這個協(xié)議的只需要對SafeObjectProxy做個Category實現(xiàn)一下即可。

還有就是防御的分類開啟,這時候枚舉就要用位運算的形式,這樣才能兼容多種模式并存如下只開啟Array和String的防御

1 [SafeObjectProxy?startSafeObjectProxyWithType:?SafeObjectProxyType_Array|?SafeObjectProxyType_String]

規(guī)范

另一個安保模塊的組成則應(yīng)該是對代碼規(guī)范的制定與校驗,這就需要clang來做了,不是這里主要講的,相當于多了一種Build Options的Compiler for C/C++/Objective-C屬性的選擇,用我們開發(fā)的Xcode校驗插件,檢查代碼語法上的問題直接報錯,這樣從源頭來規(guī)范化編碼。

Crash分類及防御實現(xiàn)

  • Unrecognized Selector(找不到方法)

  • UI Refresh Not In Main Thread(UI刷新不在主線程)

  • Input Parm Abnormal(入?yún)惓?

  • Dangling Pointer(野指針)

  • Abnormal Matching(異常配對)

  • Thread Conflict(線程沖突)

想要防御crash,首先要做的就是了解都有哪些情況會產(chǎn)生crash,上邊就是筆者總結(jié)的幾種最常見的情況,不全的話希望有人留言補足,畢竟crash的防御真正有發(fā)言權(quán)開發(fā)這種模塊的估計只有大公司開發(fā)app的,不然用戶量不夠沒樣本采集,沒法了解坑爹的情況!

而上面列的6種常見crash,真正能廣域控制得了的恐怕也只有一半不到!下面就一一講解一下,Hook切面就是主要的手段!

Unrecognized Selector(找不到方法)

這個找不到方法算是比較好辦的。。。也算是比較常見的好查的,另外處理ok了null對象調(diào)用的問題也會隨之解決
可選的方法有兩種
Hook這兩個方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
或Hook這一個方法
-(id)forwardingTargetForSelector:(SEL)aSelector

核心思想就是在找不到方法之前創(chuàng)建方法確保繼續(xù)執(zhí)行不掛,為了盡量不多余的創(chuàng)建方法,集中的把創(chuàng)建打到統(tǒng)一的地方。

前者需要在methodSignatureForSelector執(zhí)行前在新的target里創(chuàng)建沒有的方法,然后用它調(diào)用methodSignatureForSelector返回,而這里的target當然要單例弄出來省的以后來回創(chuàng)建。然后在forwardInvocation里用他來調(diào)用invokeWithTarget指到我們新的target上。

后者也就是我用的方法,之所以用它主要是一個方法 就ok!而我們還要兼顧靜態(tài)方法和實例方法去分別hook才能防住這兩種,而前者也要hook的方法更多。。。。
而這里只需要切forwardingTargetForSelector方法,靜態(tài)方法返回class,動態(tài)方法返回target,當然返回之前我們要添加上不存在的方法,值得注意的是OSX上一個神奇的問題,我在判斷是否系統(tǒng)有這個方法的時候第一次居然respondsToSelector返回false而methodSignatureForSelector有數(shù)據(jù),第二次校驗是methodSignatureForSelector才為空,而iOS上則沒這問題第一次校驗就是對的!

UI Refresh Not In Main Thread(UI刷新不在主線程)

刷新UI不在主線程的情況這里只是針對UIView和NSView的3個方法做切面線程判斷。分別是setNeedsLayout,setNeedsDisplay,setNeedsDisplayInRect,執(zhí)行之前看是不是在主線程,不在的話就切到主線程執(zhí)行,但很明顯這3個方法肯定覆蓋不全,而且就算覆蓋全了每次都判斷一下也是性能浪費,所以這里各自斟酌處理吧,這類情況暫時沒想到其他好的處理方式!但好在算是有這么個可控方案!

Input Parm Abnormal(入?yún)惓?

入?yún)惓_@是一大類,防御的方法也相對比較通俗易懂,也是最容易查最容易出現(xiàn)的。

常用類型入?yún)惓?/span>

常見類包括String,Array,Dictionary,URL,FileManager等這些類空值初始化,越界取值,空賦值等,基本看crash log統(tǒng)計依次切面對應(yīng)方法在執(zhí)行前判斷一下就ok。如objectAtIndex,objectAtIndexedSubscript,removeObjectAtIndex,fileURLWithPath,initWithAttributedString,substringFromIndex,substringToIndex等等。唯一需要注意的就是這些要切面的類名可是五花八門而且更iOS版本有很大關(guān)系,所以這個就是靠crash log積累了解有哪些坑。當然代碼寫的好就用不到了!__NSSingleObjectArrayI這個就是最近在iOS11上新發(fā)現(xiàn)的報錯數(shù)組類,當然也可能是最近我司有人寫出了這個相關(guān)的bug......
常見的需要注意的hook的類有以下
objc_getClass("__NSPlaceholderArray")
objc_getClass("__NSSingleObjectArrayI")
objc_getClass("__NSArrayI")
objc_getClass("__NSArrayM")
objc_getClass("__NSPlaceholderDictionary")
objc_getClass("__NSDictionaryI")
objc_getClass("__NSDictionaryM")
objc_getClass("NSConcreteAttributedString")
objc_getClass("NSConcreteMutableAttributedString")
objc_getClass("__NSCFConstantString")
objc_getClass("NSTaggedPointerString")
objc_getClass("__NSCFString")
objc_getClass("NSPlaceholderMutableString")
具體有哪些方法需要切面還是看源碼吧,這部分是沒什么難點的。

另外我的防御里面沒對NSCache做,可能以后會隨便加點,因為緩存相關(guān)的模塊我的建議是自己封裝緩存模塊或用第三方,那樣對于上層使用者來說已經(jīng)是安全的了!各種異常處理在緩存模塊里就應(yīng)該有封裝。

KVC Crash

KVC歸根結(jié)底也算這類入?yún)惓?#xff0c;一共切面3個地方就夠防御了!
-(void)setValue:(id)value forKey:(NSString *)key,
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath
空值防御上面2個方法
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
上面這個就是沒有的屬性做賦值操作時走的回調(diào),如果用到我的SafeObjectProxy要自定義各個類不同的處理是可以不開啟UndefinedKey防御的!

Dangling Pointer(野指針)

這個種Crash堪稱經(jīng)典!就是那個最難排查的,而這里我們能做的防御事情也十分有限!
具體定位看看騰訊這幾篇很有幫助!
如何定位Obj-C野指針隨機Crash(一)
如何定位Obj-C野指針隨機Crash(二)
如何定位Obj-C野指針隨機Crash(三)
我們只能去對已知的出現(xiàn)野指針的類進行防御,找到crash的野指針開啟Zombie Objects,加上Zombies工具,然后想辦法不斷提高復(fù)現(xiàn)率還是可以的定位到的。
我們的防御則是hook系統(tǒng)dealloc,判斷需要做處理的類不走系統(tǒng)delloc而是走objc_desctructInstance釋放實例內(nèi)部所持有屬性的引用和關(guān)聯(lián)對象,保證對象最小化。緊接著就需要來波isa swizzling了,因為通常野指針伴隨著的還有就是調(diào)用沒有的方法,或者由于調(diào)用的這個時機是不正常的,各種數(shù)據(jù)的安全性都沒了保證,所以dealloc后解除所有持有,再把原來的isa指向一個其他的類,而這個類能把所有的調(diào)用方法指向一個空方法這樣就起到了防御的作用。

能干這事的也只有NSProxy了,利用協(xié)議實現(xiàn)methodSignatureForSelector,forwardInvocation方法,統(tǒng)一打到之前處理找不到方法自動創(chuàng)建的類中,也就是在NSProxy內(nèi)實現(xiàn)上面Unrecognized Selector的防御,這樣所有對于野指針的調(diào)用就都是空了!
正因為上面的原因一旦開啟了這個防御,真正釋放的時機就還是有的,如果在野指針出現(xiàn)前觸發(fā)了真正釋放的邏輯,crash就還是會有的!
我在SafeObjectProxy里只是用野指針個數(shù)控制做真正釋放,回頭可能會封裝個block方便復(fù)雜情況的判斷。

Abnormal Matching(異常配對)

這一類算是不建議做防御的!成對的方法處理異常像KVO,NSTimer,NSNotification都算,需要注冊和注銷。
這種情況我的建議是統(tǒng)一封裝獨立模塊調(diào)用統(tǒng)一的方法,讓人不需要關(guān)心注冊和注銷,主要寫邏輯處理。從功能實現(xiàn)上做嚴格限制,這樣讓人考慮的就是怎么樣把一個場景融入到封裝的方法中,而不是隨意的寫!
下面說下原因,由于注冊和注銷是分離寫的 ,所以使用場景,解決問題的方法都會有著非常靈活的操作,這其實很可怕,先用KVO做一個舉例順便說一下這類防御如果真要做一般的做法是怎么做。

KVO

KVO這種crash如果要防御其實只能防御下面3種情況:
1.觀察者或被觀察者已經(jīng)不存在了
2.取消和添加的次數(shù)不匹配
3.沒寫監(jiān)聽回調(diào)observeValueForKeyPath:ofObject:change:context:

而這3種情況我們來認真思考下開發(fā)的階段是不是貌似都會第一時間就被發(fā)現(xiàn)!而且如果是沒經(jīng)驗的程序員寫KVO我們是不是都不敢用,會再三審查,而有經(jīng)驗的又不會犯上面的錯。。。。
如果對上面的情況防御也很復(fù)雜,而且我嘗試并且用過很多第三方,都在我司稍微有點復(fù)雜的項目上掛了,不僅沒能防御crash還造了crash,這種成對邏輯的靈活性非常高,你沒法知道系統(tǒng)內(nèi)部人家怎么用著玩的!
說一下防御上面的情況首先是吧,切面add、removeObserve是一定的,還要在所有的類里對再加一個對象,這個對象主要負責管理KVO下面就叫KVOController吧,讓所有的觀察者都成為了被觀察者的一個屬性,用map記錄原來的觀察者和keyPath等信息,這樣添加或移除觀察者就能判斷是不是成對出現(xiàn)的,另外KVOController在dealloc時也可以通過map依次移除監(jiān)聽,而由于所有的監(jiān)聽回調(diào)其實都是由KVOController的observeValueForKeyPath:ofObject:change:context:通過[originObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context]傳遞出去的自然沒寫監(jiān)聽回調(diào)的情況也可以判斷了,但也是能解決那3個情況!

真正KVO產(chǎn)生的恐怖的crash是移除時機不和觀察者或被觀察者銷毀有關(guān)系,而是跟我們的邏輯有關(guān),一旦沒在合適時機移除導(dǎo)致的crash排查起來超級費勁!還有你在監(jiān)聽回調(diào)里處理邏輯有沒有線程安全問題,這些才是我們在上線前容易漏,排查又不好排查的!

安保系統(tǒng)則是要保護上線后能正常運作,然而就像我這里說的KVO,如果不在編碼期間就做嚴格規(guī)范,上線后出的問題也是根本無從防御的!

然后再來說說怎么限制我們的自由發(fā)揮,KVOController剛才說到的這里需要的是把它變形,把回調(diào)用block放出來,另外就是讓它有單例模式和普通的實例模式,只有創(chuàng)建對象、關(guān)聯(lián)監(jiān)聽和邏輯處理,一個KVOController可以是全局或?qū)儆谝粋€對象,相當于可視化了KVO的生效周期,一目了然,這里讓特殊邏輯適應(yīng)我們的規(guī)范才是正確的安保思路。包括NSTimer在內(nèi)也也是如此可以搞個TimerController不過封裝最好也別用NSTimer精度不高,反正要封裝不如直接gcd,與其要手動保持成對不如我們就把邏輯封裝好,讓使用者忘掉成對的概念!但在開放的今天完全可以GitHub搜一波找些封裝好的自己再簡單包裝下,然后讓團隊遵循規(guī)范開發(fā)即可。。。

KVO:KVOController比較推薦的一個KVO管理

NSTimer

NSTimer比較特殊,有些時候偏偏不該成對使用,它的成對的邏輯其實是跟自己的生命周期有關(guān),畢竟生命周期結(jié)束時要去成對的停掉timer才能釋放,另一點就是NSTimer精確度并不高!但它封裝出來給人用的方法是ok的正是有單例模式和實例模式兩種使用。所以我的建議當然是自己把gcd的timer封裝一下,另外把target這個概念變?yōu)閣eak持有,這樣我們自己封裝的timer就可以dealloc的時候停掉timer釋放了,按照系統(tǒng)NSTimer封裝方法即可。這樣至少能保證timer指定的target釋放時timer能停掉不會因為跑了其他不安全的邏輯掛掉。其他可能掛掉的情況應(yīng)該比較少。。。

Timer:MSWeakTimer比較推薦的一個計時器封裝方法就是我上面講的那種

NSNotification

這個雖然也是成對使用,單比上面的幾個要安全一些,因為使用它有[[NSNotificationCenter defaultCenter] removeObserver:self]多次調(diào)用或沒addObserver都不會掛,所以可以全局搞一下,我在SafeObjectProxy里面就只是對所有NSObject對象添加了個屬性做標識,然后hook一下NSNotificationCenter的-(void)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject方法,只要observer是NSObject對象我就標識一下,然后切所有NSObject的dealloc只要標識了的統(tǒng)一執(zhí)行[[NSNotificationCenter defaultCenter] removeObserver:self],反正多執(zhí)行了也沒問題用的放心!

但只要是成對的,就有另一個問題,萬一真正需要注銷的地方是跟邏輯有關(guān),那你對象銷毀時注銷早就晚了,就像上面KVO中提到的我們做的這層crash防御其實犯錯率并不高能及時發(fā)現(xiàn),而及時發(fā)現(xiàn)不了的只能是通過編碼規(guī)范或者人員分級禁用來解決。

Thread Conflict(線程沖突)

基本無解的問題,出現(xiàn)以后瞬間懵逼,典型例子就是死鎖,異步調(diào)用同一對象導(dǎo)致不安全,基本沒有防御手段,排查也只能靠多加log不斷復(fù)現(xiàn),然后猜。。。。
但一般只要代碼按照正常的規(guī)范寫也不會那么容易遇到這問題,但線程沖突理論上只要保證UI操作都在主線程,其他都gcd不在主線程上,然后部分需要線程安全的gcd信號量做鎖就可以,但不會有人這樣寫代碼,性能和效率那么搞是都要廢的,現(xiàn)在都恨不得你馬上出活那有空那樣,這類就可以完全不考慮防御的事了!


總結(jié)

以上是生活随笔為你收集整理的【iOS】Cocoa(iOS,OSX)安保系统设计实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产一区二区小视频 | 天堂а√在线中文在线鲁大师 | 亚洲逼逼 | 一区二区欧美日韩 | 古装做爰无遮挡三级 | 国产欧美久久久 | 日韩视频免费观看高清完整版在线观看 | 天堂中文在线网 | 污污视频网站在线 | 欧美性猛交ⅹxxx乱大交3 | 丰满女邻居的色诱4hd | 伊人春色网 | 特黄一级视频 | 中文字幕资源网 | 国产亚洲欧美精品久久久www | 亚洲综合五月 | 久久中文网 | 制服诱惑一区二区 | 好男人www社区在线视频夜恋 | 精品久久久久久久久久久久久久久久久 | 丁香婷婷六月 | 成人黄色片在线观看 | 国产综合精品在线 | 亚洲蜜桃精久久久久久久久久久久 | 男男车车的车车网站w98免费 | 日韩熟女一区二区 | 依人在线| 视频一区国产 | 色屁屁一区二区三区 | 亚洲久久久久久 | 日本道中文字幕 | 97精品超碰一区二区三区 | 精品人伦一区二区三区蜜桃免费 | 诱人的乳峰奶水hd | 日日操夜夜操狠狠操 | 亚洲色图40p| 欧美在线v| 国产精品一二三四区 | 捆绑调教在线观看 | 成人18视频免费69 | 久久久中文字幕 | 国内性爱视频 | 亚洲第一视频 | 亚洲色婷婷一区二区三区 | 亚洲射色| 日韩三区在线 | 国产污视频在线观看 | 黑人玩弄人妻一区二区三区影院 | 精品精品视频 | 久久亚洲激情 | 97超碰97 | 人妻 校园 激情 另类 | 成年人网站黄 | 国产在线成人精品午夜 | 在线播放一级片 | 日本a网| 人妻少妇精品一区二区三区 | 日韩精品一区三区 | 深夜福利一区 | 91人人澡人人爽人人精品 | 国产三级影院 | 久草热在线视频 | 动漫同人高h啪啪爽文 | 国产白丝av | 在线看日本 | 国内自拍青青草 | 久久久午夜电影 | 日韩欧美精品在线 | 久久天天干| 国产精品国产三级国产 | 麻豆视频免费在线观看 | 黄色三级三级 | 色婷婷av国产精品 | 天堂在线8 | 亚洲一区二区在线看 | 99精品视频免费版的特色功能 | 久操新在线 | 美国成人免费视频 | juliaann欧美二区三区 | 欧美一级看片 | 国产av一区二区三区精品 | 男人和女人在床的app | 欧美精品一区二区三区久久久竹菊 | 夜色综合网 | 亚洲一级片在线播放 | 天天看黄色片 | 波多野 在线 | 久久国产精品影院 | 暖暖免费观看日本版 | 日韩中文三级 | 国内自拍真实伦在线观看 | 中文字幕av久久爽一区 | 亚洲一区免费 | 亚洲天堂欧美 | 夜色成人网 | 天天色天天搞 | 成人精品水蜜桃 | 在线不卡欧美 | 高潮喷水一区二区三区 |