【移動應用開發(fā)技術】iOS APP運行時Crash自動修復系統(tǒng)_第1頁
【移動應用開發(fā)技術】iOS APP運行時Crash自動修復系統(tǒng)_第2頁
【移動應用開發(fā)技術】iOS APP運行時Crash自動修復系統(tǒng)_第3頁
【移動應用開發(fā)技術】iOS APP運行時Crash自動修復系統(tǒng)_第4頁
【移動應用開發(fā)技術】iOS APP運行時Crash自動修復系統(tǒng)_第5頁
已閱讀5頁,還剩13頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

【移動應用開發(fā)技術】iOSAPP運行時Crash自動修復系統(tǒng)

大白(

Baymax),迪士尼動畫《超能陸戰(zhàn)隊》中的健康機器人,是一個體型胖胖的充氣機器人,因呆萌的外表和善良的本質獲得大家的喜愛,被稱為“萌神”。

Baymax項目是為了減少開發(fā)人員在開發(fā)中一些不規(guī)范的代碼編寫造成的內存泄露,界面卡頓,耗電等問題而來的一個監(jiān)控系統(tǒng)。現在Baymax迎來了它新的功能:APP運行時Crash自動防護功能,為app的流程順利運行保駕護航!下面將詳細介紹一下APP運行時Crash自動修復系統(tǒng)開發(fā)的目的,設計的原理以及使用的方法。是否存在這樣的夜晚,當剛剛躺下準備美美的睡一覺的時候,突然來一記奪命電話Call,一接起來發(fā)現是你老板!!!“小王啊,剛剛上線的X.X.X版本出問題了啊,怎么樣操作會crash啊,導致新功能都無法使用了,快定位一下是什么原因,抓緊hotpatch修復一下啊!”。心里一萬頭草泥馬呼嘯而過,瞬間已經滿頭大汗的你卻還要故作鎮(zhèn)靜地回答:“嗯,老板我馬上去看看,一定努力解決問題!”急忙打開電腦的你,知道今夜注定無眠了。是否又存在這樣的情形,你老板把大家都聚起來開了一個年初KPI目標制定會議,說到:“作為一個資深的技術團隊,app性能是我們技術團隊首抓的目標,其中很最要的一項就是app的崩潰率,去年我們app統(tǒng)計出來的崩潰率是千分之五,而我們的競爭對手的崩潰率只有萬分之五,相差了10倍!今年我們要趕超他們,最起碼也要和他們持平。”你甚是贊同,但是你心里卻又有點懷疑,對方的開發(fā)資源是我們的好幾倍而且個個都是資深老司機,我們團隊里卻大多都是應屆生小鮮肉,這KPI能完成么?如果你遇到過以上的情況并且對此深表頭痛的話,那么大白健康系統(tǒng)—APP運行時Crash自動修復系統(tǒng)將會是你的不二選擇!APP運行時Crash自動修復+捕獲系統(tǒng)的設計初衷,就是為了降低app的crash率。利用Objective-C語言的動態(tài)特性,采用

AOP(AspectOrientedProgramming)面向切面編程的設計思想,做到無痕植入。能夠

自動在app運行時實時捕獲導致app崩潰的破環(huán)因子,然后通過特定的技術手段去化解這些破壞因子,使app免于崩潰,照樣可以繼續(xù)正常運行,為app的持續(xù)運轉保駕護航。APP運行時Crash自動修復系統(tǒng)的主要功能,可以用一句話來簡單的概括:

對業(yè)務代碼的零侵入性地將原本會導致app崩潰的crash抓取住,消滅掉,保證app繼續(xù)正常地運行,再將crash的具體信息提取出來,實時返回給用戶。通過下面的一個小例子就可以很直觀的體現出來系統(tǒng)的作用:調用以下的一段代碼結果肯定會導致app的崩潰,因為testObj是一個UIButton對象,而UIButton并沒有實現someMethod:這個方法,所以向testObj發(fā)送someMethod:這個方法的時候,將會導致該方法無法在相關的方法列表里找到,最終導致app的crash。但是通過我們的crash防護系統(tǒng),調用這段代碼時app并不會崩潰,同時XCode的Console如下:

https://upload-images.jianshu.io/upload_images/22877992-6498f3db12a17bed?imageMogr2/auto-orient/strip可見對應的crash的信息(crash類型,原因,調用棧信息)均可以完整的打印在XCode的Console中。說明我們的大白系統(tǒng)已經捕捉到了這個crash,將該crash消滅掉并且吐出來該crash的完整信息。當然目前系統(tǒng)的功能并沒有強大到可以把所有的crash都處理掉,不過一些常見的高頻次發(fā)生的crash,系統(tǒng)均會針對他們一一處理。目前可以處理掉的crash類型具體有以下幾種:unrecognizedselectorcrashKVOcrashNSNotificationcrashNSTimercrashContainercrash(數組越界,插nil等)NSStringcrash(字符串操作的crash)BadAccesscrash(野指針)UInotonMainThreadCrash(非主線程刷UI(機制待改善))對于每種類型的crash,安全系統(tǒng)都采取不同的方式,進行了對應的處理。具體的處理細節(jié)詳見下章:

Chapter3-實現原理前面已經提過,目前的安全防護系統(tǒng)可以覆蓋到8中類型的Crash,分別為:unrecognizedselectorcrashKVOcrashNSNotificationcrashNSTimercrashContainercrash(數組越界,插nil等)NSStringcrash(字符串操作的crash)BadAccesscrash(野指針)UInotonMainThreadCrash(非主線程刷UI(機制待改善))接下來將一一詳細介紹這8種類型的Crash的防護的實現的具體原理:unrecognizedselector類型的crash在app眾多的crash類型中占著比較大的成分,通常是因為一個對象調用了一個不屬于它方法的方法導致的。例如調用以下一段代碼就會產生crash具體crash時的表現見下圖:

https://upload-images.jianshu.io/upload_images/22877992-eddd08671a128c46?imageMogr2/auto-orient/strip要解決這中類型的crash,我們需要先了解清楚它產生的具體原因和流程。讓我們看一下方法調用在運行時的過程。runtime中具體的方法調用流程大致如下:

1.首先,在相應操作的對象中的緩存方法列表中找調用的方法,如果找到,轉向相應實現并執(zhí)行。

2.如果沒找到,在相應操作的對象中的方法列表中找調用的方法,如果找到,轉向相應實現執(zhí)行

3.如果沒找到,去父類指針所指向的對象中執(zhí)行1,2.

4.以此類推,如果一直到根類還沒找到,轉向攔截調用,走消息轉發(fā)機制。

5.如果沒有重寫攔截調用的方法,程序報錯。在方法調用中說到了,如果沒有找到方法就會轉向攔截調用。那么什么是攔截調用呢?攔截調用就是,在找不到調用的方法程序崩潰之前,你有機會通過重寫NSObject的四個方法來處理:攔截調用的整個流程即Objective——C的消息轉發(fā)機制。其具體流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-127579934e04e8c8?imageMogr2/auto-orient/strip由上圖可見,在一個函數找不到時,runtime提供了三種方式去補救:

1、調用resolveInstanceMethod給個機會讓類添加這個實現這個函數

2、調用forwardingTargetForSelector讓別的對象去執(zhí)行這個函數

3、調用forwardInvocation(函數執(zhí)行器)靈活的將目標函數以其他形式執(zhí)行。

如果都不中,調用doesNotRecognizeSelector拋出異常。既然可以補救,我們完全也可以利用消息轉發(fā)機制來做文章。那么問題來了,在這三個步驟里面,選擇哪一步去改造比較合適呢。這里我們選擇了第二步forwardingTargetForSelector來做文章。原因如下:resolveInstanceMethod需要在類的本身上動態(tài)添加它本身不存在的方法,這些方法對于該類本身來說冗余的forwardInvocation可以通過NSInvocation的形式將消息轉發(fā)給多個對象,但是其開銷較大,需要創(chuàng)建新的NSInvocation對象,并且forwardInvocation的函數經常被使用者調用,來做多層消息轉發(fā)選擇機制,不適合多次重寫forwardingTargetForSelector可以將消息轉發(fā)給一個對象,開銷較小,并且被重寫的概率較低,適合重寫選擇了forwardingTargetForSelector之后,可以將NSObject的該方法重寫,做以下幾步的處理:

1.動態(tài)創(chuàng)建一個樁類

2.動態(tài)為樁類添加對應的Selector,用一個通用的返回0的函數來實現該SEL的IMP

3.將消息直接轉發(fā)到這個樁類對象上。流程圖如下:

https://upload-images.jianshu.io/upload_images/22877992-b5d87af2a3fda168?imageMogr2/auto-orient/strip注意如果對象的類本事如果重寫了forwardInvocation方法的話,就不應該對forwardingTargetForSelector進行重寫了,否則會影響到該類型的對象原本的消息轉發(fā)流程。通過重寫NSObject的forwardingTargetForSelector方法,我們就可以將無法識別的方法進行攔截并且將消息轉發(fā)到安全的

樁類對象中,從而可以使app繼續(xù)正常運行。KVO,即:Key-ValueObserving,它提供一種機制,當指定的對象的屬性被修改后,則對象就會接受收到通知。簡單的說就是每次指定的被觀察的對象的屬性被修改后,KVO就會自動通知相應的觀察者了。KVO機制在iOS的很多開發(fā)場景中都會被使用到。不過如果一不小心使用不當的話,會導致大量的crash問題。所以如果能找到一種方法能夠自動抓取這些由于開發(fā)者粗心所導致的KVOCrash問題的話,是有一定的價值的。首先我們來看看通過會導致KVOCrash的兩種情形:

https://upload-images.jianshu.io/upload_images/22877992-4099e4ffa538a647?imageMogr2/auto-orient/strip

https://upload-images.jianshu.io/upload_images/22877992-426e27b6c86e58d1?imageMogr2/auto-orient/strip通常一個對象的KVO關系圖如下:

https://upload-images.jianshu.io/upload_images/22877992-c6d555259f846c3e?imageMogr2/auto-orient/strip一個被觀察的對象(ObservedObject)上有若干個觀察者(Observer),每個觀察者又觀察若干條KeyPath。如果觀察者和keypath的數量一多,很容易理不清楚被觀察對象整個KVO關系,導致被觀察者在dealloc的時候,還殘存著一些關系沒有被注銷。同時還會導致KVO注冊觀察者與移除觀察者不匹配的情況發(fā)生。筆者曾經還遇到過在多線程的情況下,導致KVO重復添加觀察者或移除觀察者的情況。這類問題通常多數發(fā)生的比較隱蔽,不容易從代碼的層面去排查。由上可見多數由于KVO而導致的crash原因是由于被觀察對象的KVO關系圖混亂導致。那么如何來管理混亂的KVO關系呢。可以讓被觀察對象持有一個KVO的delegate,所有和KVO相關的操作均通過delegate來進行管理,delegate通過建立一張map來維護KVO整個關系。如下圖:

https://upload-images.jianshu.io/upload_images/22877992-cbf2f3120bf54ef6?imageMogr2/auto-orient/strip這樣做的好處有兩個:1.如果出現KVO重復添加觀察者或重復移除觀察者(KVO注冊觀察者與移除觀察者不匹配)的情況,delegate可以直接阻止這些非正常的操作。2.被觀察對象dealloc之前,可以通過delegate自動將與自己有關的KVO關系都注銷掉,避免了KVO的被觀察者dealloc時仍然注冊著KVO導致的crash。被swizzle的方法分別是:關于方法改造流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-1f36e63bb832eaf5?imageMogr2/auto-orient/strip通過上面的流程,將observerd對象的所有kvo相關的observer信息全部轉移到KVOdelegate上,并且避免了相同kvoinfo被重復添加多次的可能性。關于方法改造流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-0f59bf74a2efc1d7?imageMogr2/auto-orient/strip移除一個keypath的Observer時,當delegate的kvoInfoMap中找不到key為該keypath的時候,說明此時delegate并沒有持有對應keypath的observer,即說明移除了一個不匹配的觀察者,此時如果再繼續(xù)操作會導致app崩潰,所以應該及時中斷流程,然后統(tǒng)計異常信息。當keypath對應的KVOInfo列表(infoArray)為空的時候,說明此時delegate已經不再持有任何和keypath相關的observer了。這時應該調用原有removeObserver的方法將delegate對應的觀察者移除。注意到在檢查遍歷infoArray的時侯,除了要刪除對應的info信息,還多了一步檢查info.observer==nil的過程,是因為如果observer為nil,那么此時如果keypath對應的值變化的話,也會因為找不到observer而崩潰,所以需要做這一步來阻止該種情況的發(fā)生。關于方法改造流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-f342011a21cb123f?imageMogr2/auto-orient/stripdelegate對于observeValueForKeyPath方法的修改最主要的地法規(guī),在于將對應的響應方法轉移給真正的KVOObserver,通過keyInfoMap找到keypath對應的KVOInfo里面預先存儲好的observer,然后調用observer原本的響應方法同時在遍歷InfoArray的時候,發(fā)現info.observerw==nil的時候,需要及時將其清除掉,避免KVO的觀察者observer被釋放后value變化導致的crash最后,針對

KVO的被觀察者dealloc時仍然注冊著KVO導致的crash的情況可以將NSObject的deallocswizzle,在objectdealloc的時候自動將其對應的kvodelegate所有和kvo相關的數據清空,然后將kvodelegate也置空。避免出現KVO的被觀察者dealloc時仍然注冊著KVO而產生的crash當一個對象添加了notification之后,如果dealloc的時候,仍然持有notification,就會出現NSNotification類型的crash。NSNotification類型的crash多產生于程序員寫代碼時候犯疏忽,在NSNotificationCenter添加一個對象為observer之后,忘記了在對象dealloc的時候移除它。所幸的是,蘋果在iOS9之后專門針對于這種情況做了處理,所以在iOS9之后,即使開發(fā)者沒有移除observer,Notificationcrash也不會再產生了。不過針對于iOS9之前的用戶,我們還是有必要做一下NSNotificationCrash的防護的。NSNotificationCrash的防護原理很簡單,利用methodswizzlinghookNSObject的dealloc函數,再對象真正dealloc之前先調用一下

[[NSNotificationCenterdefaultCenter]removeObserver:self]即可。

注意到并不是所有的對象都需要做以上的操作,如果一個對象從來沒有被NSNotificationCenter添加為observer的話,在其dealloc之前調用removeObserver完全是多此一舉。所以我們hook了NSNotificationCenter的

addObserver:(id)observerselector:(SEL)aSelectorname:(NSString*)aNameobject:(id)anObject函數,在其添加observer的時候,對observer動態(tài)添加標記flag。這樣在observerdealloc的時候,就可以通過flag標記來判斷其是否有必要調用removeObserver函數了。在程序開發(fā)過程中,大家會經常使用定時任務,但使用NSTimer的scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:接口做重復性的定時任務時存在一個問題:NSTimer會強引用target實例,所以需要在合適的時機invalidate定時器,否則就會由于定時器timer強引用target的關系導致target不能被釋放,造成內存泄露,甚至在定時任務觸發(fā)時導致crash。crash的展現形式和具體的target執(zhí)行的selector有關。與此同時,如果NSTimer是無限重復的執(zhí)行一個任務的話,也有可能導致target的selector一直被重復調用且處于無效狀態(tài),對app的CPU,內存等性能方面均是沒有必要的浪費。所以,很有必要設計出一種方案,可以有效的防護NSTimer的濫用問題。上面的分析可見,NSTimer所產生的問題的主要原因是因為其沒有再一個合適的時機invalidate,同時還有NSTimer對target的強引用導致的內存泄漏問題。那么解決NSTimer的問題的關鍵點在于以下兩點:NSTimer對其target是否可以不強引用是否找到一個合適的時機,在確定NSTimer已經失效的情況下,讓NSTimer自動invalidate關于第一個問題,target的強引用問題。可以用如下圖的方案來解決:

https://upload-images.jianshu.io/upload_images/22877992-d8536eb902828998?imageMogr2/auto-orient/strip在NSTimer和target之間加入一層stubTarget,stubTarget主要做為一個橋接層,負責NSTimer和target之間的通信。同時NSTimer強引用stubTarget,而stubTarget弱引用target,這樣target和NSTimer之間的關系也就是弱引用了,意味著target可以自由的釋放,從而解決了循環(huán)引用的問題。上文提到了stubTarget負責NSTimer和target的通信,其具體的實現過程又細分為兩大步:

step1.swizzleNSTimer中scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:相關的方法,在新方法中動態(tài)創(chuàng)建stubTarget對象,stubTarget對象弱引用持有原有的target,selector,timer,targetClass等properties。然后將原target分發(fā)stubTarget上,selector回調函數為stubTarget的fireProxyTimer:,流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-94ab1b478b6d336e?imageMogr2/auto-orient/strip

step2.通過stubTarget的fireProxyTimer:來具體處理回調函數selector的處理和分發(fā),流程如下圖:

https://upload-images.jianshu.io/upload_images/22877992-9fe808c515a7b6a4?imageMogr2/auto-orient/strip因為stubTarget的介入,原有的target已經可以不受NSTimer強引用的牽制,而自由的釋放。由上圖流程可知,當NSTimer的回調函數fireProxyTimer:被執(zhí)行的時候,會自動判斷原target是否已經被釋放,如果釋放了,意味著NSTimer已經無效,此時如果還繼續(xù)調用原有target的selector很有可能會導致crash,而且是沒有必要的。所以此時需要將NSTimerinvalidate,然后統(tǒng)計上報錯誤數據。如此一來就做到了NSTimer在合適的時機自動invalidate。Container類型的crash指的是容器類的crash,常見的有NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache的crash。一些常見的越界,插入nil,等錯誤操作均會導致此類crash發(fā)生。由于產生的原因比較簡單,就不展開來描述了。該類crash雖然比較容易排查,但是其在appcrash概率總比還是挺高,所以有必要對其進行防護。Containercrash類型的防護方案也比較簡單,針對于NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache的一些常用的會導致崩潰的API進行methodswizzling,然后在swizzle的新方法中加入一些條件限制和判斷,從而讓這些API變的安全,這里就不展開來具體描述了。NSString/NSMutableString類型的crash的產生原因和防護方案與Containercrash很相像,這里也不展開來描述了。在App的所有Crash中,訪問野指針導致的Crash占了很大一部分,野指針類型crash的表現為:ExceptionType:SIGSEGV,ExceptionCodes:SEGV_ACCERR或者如下圖:

https://upload-images.jianshu.io/upload_images/22877992-9121cefe5a645bc3?imageMogr2/auto-orient/strip解決野指針導致的crash往往是一件棘手的事情,一來產生crash的場景不好復現,二來crash之后console的信息提供的幫助有限。XCode本身為了便于開放調試時發(fā)現野指針問題,提供了Zombie機制,能夠在發(fā)生野指針時提示出現野指針的類,從而解決了開發(fā)階段出現野指針的問題。然而針對于線上產生的野指針問題,依舊沒有一個比較好的辦法來定位問題。所以,因為野指針出現概率高而且難定位問題,非常有必要針對于野指針專門做一層防護措施。野指針問題的解決思路方向其實很容易確定,XCode提供了Zombie的機制來排查野指針的問題,那么我們這邊可以實現一個類似于Zombie的機制,加上對zombie實例的全部方法攔截機制和消息轉發(fā)機制,那么就可以做到在野指針訪問時不Crash而只是crash時相關的信息。同時還需要注意一點:因為zombie的機制需要在對象釋放時保留其指針和相關內存占用,隨著app的進行,越來越多的對象被創(chuàng)建和釋放,這會導致內存占用越來越大,這樣顯然對于一個正常運行的app的性能有影響。所以需要一個合適的zombie對象釋放機制,確定zombie機制對內存的影響是有限度的。improve版的zombie機制的實現主要分為以下四個環(huán)節(jié):

step1.methodswizzling替換NSObject的allocWithZone方法,在新的方法中判斷該類型對象是否需要加入野指針防護,如果需要,則通過objc_setAssociatedObject為該對象設置flag標記,被標記的對象后續(xù)會進入zombie流程流程圖如下:

https://upload-images.jianshu.io/upload_images/22877992-2c4a13c9f9888360?imageMogr2/auto-orient/strip做flag標記是因為很多系統(tǒng)類,比如NSString,UIView等創(chuàng)建,釋放非常頻繁,而這些實例發(fā)生野指針概率非常低。基本都是我們自己寫的類才會有野指針的相關問題,所以通過在創(chuàng)建時設置一個標記用來過濾不必要做野指針防護的實例,提高方案的效率。同時做判斷是否要加入標記的條件里面,我們加入了黑名單機制,是因為一些特定的類是不適用于添加到zombie機制的,會發(fā)生崩潰(例如:NSBundle),而且所以和zombie機制相關的類也不能加入標記,否則會在釋放過程中循環(huán)引用和調用,導致內存泄漏甚至棧溢出。

step2.methodswizzling替換NSObject的dealloc方法,對flag標記的對象實例調用objc_destructInstance,釋放該實例引用的相關屬性,然后將實例的isa修改為HTZombieObject。通過objc_setAssociatedObject保存將原始類名保存在該實例中。流程圖如下:

https://upload-images.jianshu.io/upload_images/22877992-1ecb7622eb98e63f?imageMogr2/auto-orient/strip

調用objc_destructInstance的原因:這里參考了系統(tǒng)在Object-CRuntime中NSZombies實現,dealloc最后會調到

objectdispose函數,在這個函數里面其實也做了三件事情,1)調用objc_destructInstance釋放該實例引用的相關實例2)將該實例的isa修改為stubClass,接受任意方法調用3)釋放該內存官方文檔對objc_destructInstance的解釋為:

Destroysaninstanceofaclasswithoutfreeingmemoryandremovesanyassociatedreferencesthisinstancemighthavehad.說明objc_destructInstance會釋放與實例相關聯的引用,但是并不釋放該實例等內存。

step3.在HTZombieObject通過消息轉發(fā)機制forwardingTargetForSelector處理所有攔截的方法,根據selector動態(tài)添加能夠處理方法的響應者HTStubObject實例,然后通過objc_getAssociatedObject獲取之前保存該實例對應的原始類名,統(tǒng)計錯誤數據。流程圖如下:

https://upload-images.jianshu.io/upload_images/22877992-aeac28cb1e4377d5?imageMogr2/auto-orient/stripHTZombieObject的處理和unrecognizedselectorcrash的處理是一樣,主要的目的就是攔截所有傳給HTZombieObject的函數,用一個返回為空的函數來替換,從而達到程序不崩潰的目的。

step4.當退到后臺或者達到未釋放實例的上限時,則在ht_freeSomeMemory方法中調用原有dealloc方法釋放所有被zombie化的實例綜上所述,可以用下圖總結一下badaccess類型crash的防護流程:

https://upload-images.jianshu.io/upload_images/22877992-6c373e7785d762f7?imageMogr2/auto-orient/strip

1.做了野指針防護,通過動態(tài)插入一個空實現的方法來防止出現Crash,但是業(yè)務層面的表現難以確定,可能會進入業(yè)務異常的狀態(tài)。需要擬定一下如何展現該問題給用戶的方案

2.由于做了延時釋放若干實例,對系統(tǒng)總內存會產生一定影響,目前將內存的緩沖區(qū)開到2M左右,所以應該沒有很大的影響,但還是可能潛在一些風險。

3.延時釋放實例是根據相關功能代碼會聚焦在某一個時間段調用的假設前提下,所以野指針的zombie保護機制只能在其實例對象仍然緩存在zombie的緩存機制時才有效,若在實例真正釋放之后,再調用野指針還是會出現Crash。在非主線程刷UI將會導致app運行crash,有必要對其進行處理。目前初步的處理方案是swizzleUIView類的以下三個方法:在這三個方法調用的時候判斷一下當前的線程,如果不是主線程的話,直接利用

dispatch_async(dispatch_get_main_queue(),^{//調用原本方法});來將對應的刷UI的操作轉移到主線程上,同時統(tǒng)計錯誤信息。但是真正實施了之后,發(fā)現這三個方法并不能完全覆蓋UIV

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論