在講解卡頓優(yōu)化之前,我們先來(lái)思考一下,在iOS中,屏幕是怎么成像的呢
CPU和GPU在屏幕成像的過(guò)程中,CPU和GPU起著至關(guān)重要的作用
CPU(Central Processing Unit,中央處理器)CPU的主要任務(wù)是進(jìn)行對(duì)象的創(chuàng)建和銷(xiāo)毀、對(duì)象屬性的調(diào)整、布局計(jì)算、文本的計(jì)算和排版、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制(Core Graphics)
GPU(Graphics Processing Unit,圖形處理器)GPU的主要任務(wù)是對(duì)紋理的渲染##### CPU和GPU的關(guān)系我們所看到的成像,都是通過(guò)CPU和GPU共同協(xié)作才能完成的
一般是經(jīng)過(guò)CPU的計(jì)算和處理好的數(shù)據(jù),交給GPU進(jìn)行渲染,然后放到幀的緩存區(qū),再被視頻控制器讀取,才能顯示到我們的屏幕上
在iOS中的幀緩存屬于雙緩沖機(jī)制,有前幀緩存和后幀緩存;GPU會(huì)分情況進(jìn)行選取用哪塊緩存,這樣執(zhí)行效率會(huì)更高一些
屏幕成像原理在iOS中的屏幕成像是由許多幀共同組成的。每一幀都會(huì)由屏幕先發(fā)出一個(gè)垂直同步信號(hào),然后再發(fā)出很多行水平同步信號(hào),每一行水平同步信號(hào)表示處理完一行的數(shù)據(jù),直到屏幕發(fā)完所有的水平同步信號(hào),表示這一幀的數(shù)據(jù)全部處理完成了,再會(huì)進(jìn)行下一輪的垂直同步信號(hào)的發(fā)出,表示即將處理下一幀的數(shù)據(jù)
卡頓產(chǎn)生的原因在圖像處理過(guò)程中,CPU處理計(jì)算數(shù)據(jù)會(huì)消耗一定時(shí)間,然后再交由GPU,而GPU進(jìn)行渲染也會(huì)花費(fèi)時(shí)間,所以CPU和GPU都完成已經(jīng)消耗了一定的時(shí)間;
而屏幕的垂直同步信號(hào)發(fā)出的時(shí)間如果正好是CPU、GPU處理完的時(shí)間,那么就會(huì)完好的先該幀圖像顯示出來(lái);如果CPU、GPU處理的時(shí)間過(guò)長(zhǎng)并且沒(méi)有完全處理完,而這時(shí)垂直同步信號(hào)已經(jīng)發(fā)出了,那么就會(huì)讀取上一幀的數(shù)據(jù)進(jìn)行展示,這種現(xiàn)象叫做掉幀;
而該幀沒(méi)有處理完的數(shù)據(jù)就只能等下一個(gè)垂直同步信號(hào)再進(jìn)行讀取顯示了,這中間也會(huì)花費(fèi)一定時(shí)間進(jìn)行等待,這也是掉幀;
掉幀的現(xiàn)象就會(huì)造成卡頓,所以我們要想解決卡頓,就要盡量減少CPU和GPU的資源消耗
人眼感受不到卡頓的刷幀率平均是60FPS,表示每秒要刷60幀;通過(guò)計(jì)算相當(dāng)于每隔16ms就會(huì)有一次VSync信號(hào),也就是說(shuō)我們要在16ms內(nèi)完成CPU和GPU對(duì)數(shù)據(jù)的計(jì)算和渲染才行
優(yōu)化卡頓的具體方案關(guān)于CPU的卡頓優(yōu)化1.盡量用輕量級(jí)的對(duì)象比如用不到事件處理的地方,可以考慮使用CALayer取代UIView
還有能用基本數(shù)據(jù)類(lèi)型就不用對(duì)象類(lèi)型等等
2.不要頻繁地調(diào)用UIView的相關(guān)屬性比如frame、bounds、transform等屬性,盡量減少不必要的修改
盡量提前計(jì)算好布局,在有需要時(shí)一次性調(diào)整對(duì)應(yīng)的屬性,不要多次修改屬性盡量減少使用Autolayout,Autolayout會(huì)比直接設(shè)置frame消耗更多的CPU資源
其他需要設(shè)置的屬性最后是能確定時(shí)再賦值,不要多次更改##### 3.圖片的size最好剛好跟UIImageView的size保持一致
如果圖片本身的大小和我們給予的大小有出入,CPU會(huì)去進(jìn)行伸縮的處理,也是會(huì)消耗資源
4.控制一下線程的最大并發(fā)數(shù)量不要過(guò)多的創(chuàng)建線程,線程的創(chuàng)建和消耗也是會(huì)消耗資源的
盡量保持較少數(shù)量的線程,設(shè)置好最大并發(fā)數(shù)
如果需要長(zhǎng)期開(kāi)啟線程來(lái)執(zhí)行任務(wù),可以考慮讓線程常駐,并再不需要后再進(jìn)行統(tǒng)一銷(xiāo)毀
5.盡量把耗時(shí)的操作放到子線程比如對(duì)文本的處理(尺寸計(jì)算、繪制),都可以放到異步去做處理,例如下面代碼
// 文字計(jì)算[@"text" boundingRectWithSize:CGSizeMake(100, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil]; // 文字繪制[@"text" drawWithRect:CGRectMake(0, 0, 100, 100) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil];還有對(duì)圖片的處理,對(duì)圖片的解碼和繪制都是會(huì)消耗性能的
我們經(jīng)常使用的給UIImage賦值的方法,其本質(zhì)是會(huì)去進(jìn)行圖片的解碼和繪制的,所以我們可以將解碼繪制的過(guò)程放在子線程來(lái)處理,詳細(xì)代碼如下
// imageNamed:底層會(huì)進(jìn)行對(duì)圖片的解碼和繪制UIImageView *imageView = [[UIImageView alloc] init];imageView.image = [UIImage imageNamed:@"timg"];// 換成如下方法UIImageView *imageView = [[UIImageView alloc] init];self.imageView = imageView;dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 獲取CGImage CGImageRef cgImage = [UIImage imageNamed:@"timg"].CGImage; // alphaInfo CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage) & kCGBitmapAlphaInfoMask; BOOL hasAlpha = NO; if (alphaInfo == kCGImageAlphaPremultipliedLast || alphaInfo == kCGImageAlphaPremultipliedFirst || alphaInfo == kCGImageAlphaLast || alphaInfo == kCGImageAlphaFirst) { hasAlpha = YES; } // bitmapInfo CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; // size size_t width = CGImageGetWidth(cgImage); size_t height = CGImageGetHeight(cgImage); // context CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo); // draw CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); // get CGImage cgImage = CGBitmapContextCreateImage(context); // into UIImage UIImage *newImage = [UIImage imageWithCGImage:cgImage]; // release CGContextRelease(context); CGImageRelease(cgImage); // back to the main thread dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = newImage; });});關(guān)于GPU的卡頓優(yōu)化1.盡量減少視圖數(shù)量和層次比如一個(gè)UIView視圖我們需要?jiǎng)?chuàng)建三個(gè)圖層,減少到兩個(gè)或者一個(gè)更利于GPU的渲染性能
2.避免短時(shí)間內(nèi)大量圖片的顯示我們?cè)谔幚矶鄰垐D片時(shí),盡量避免短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張進(jìn)行顯示
3.GPU紋理尺寸的控制GPU能處理的最大紋理尺寸是4096x4096,一旦超過(guò)這個(gè)尺寸,就會(huì)占用CPU資源進(jìn)行處理,所以紋理盡量不要超過(guò)這個(gè)尺寸
4.減少透明度減少透明的視圖(alpha<1),不透明的就設(shè)置opaque為YES
像多個(gè)透明的視圖,如果有重疊部分,那么重疊部分需要重新計(jì)算展示的顏色是什么的,會(huì)消耗GPU資源
5.注意離屏渲染在OpenGL中,GPU有2種渲染方式
On-Screen Rendering:當(dāng)前屏幕渲染,在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行渲染操作Off-Screen Rendering:離屏渲染,在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作離屏渲染消耗性能的原因- 本身GPU渲染就會(huì)消耗性能
需要?jiǎng)?chuàng)建新的緩沖區(qū),又會(huì)消耗性能離屏渲染的整個(gè)過(guò)程,需要多次切換上下文環(huán)境,先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕哪些操作會(huì)觸發(fā)離屏渲染?光柵化 layer.shouldRasterize = YES遮罩layer.mask圓角,同時(shí)設(shè)置layer.masksToBounds = YES、layer.cornerRadius大于0,只滿(mǎn)足其中之一不會(huì)觸發(fā)離屏渲染考慮通過(guò)CoreGraphics繪制裁剪圓角,或者叫美工提供圓角圖片- 陰影 layer.shadowXXX如果設(shè)置了layer.shadowPath就不會(huì)產(chǎn)生離屏渲染#### 卡頓的檢測(cè)平時(shí)所說(shuō)的“卡頓”主要是因?yàn)樵谥骶€程執(zhí)行了比較耗時(shí)的操作##### 1.FPS監(jiān)控FPS的監(jiān)控,參照YYKit中的YYFPSLabel,主要是通過(guò)CADisplayLink實(shí)現(xiàn)。借助link的時(shí)間差,來(lái)計(jì)算一次刷新刷新所需的時(shí)間,然后通過(guò)刷新次數(shù) / 時(shí)間差得到刷新頻次,并判斷是否其范圍,通過(guò)顯示不同的文字顏色來(lái)表示卡頓嚴(yán)重程度。代碼實(shí)現(xiàn)如下class LLFPSLabel: UILabel { fileprivate var link: CADisplayLink = { let link = CADisplayLink.init() return link }() fileprivate var count: Int = 0 fileprivate var lastTime: TimeInterval = 0.0 fileprivate var fpsColor: UIColor = { return UIColor.green }() fileprivate var fps: Double = 0.0 override init(frame: CGRect) { var f = frame if f.size == CGSize.zero { f.size = CGSize(width: 80.0, height: 22.0) } super.init(frame: f) self.textColor = UIColor.white self.textAlignment = .center self.font = UIFont.init(name: "Menlo", size: 12) self.backgroundColor = UIColor.lightGray //通過(guò)虛擬類(lèi) link = CADisplayLink.init(target: CJLWeakProxy(target:self), selector: #selector(tick(_:))) link.add(to: RunLoop.current, forMode: RunLoop.Mode.common) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { link.invalidate() } @objc func tick(_ link: CADisplayLink){ guard lastTime != 0 else { lastTime = link.timestamp return } count += 1 //時(shí)間差 let detla = link.timestamp - lastTime guard detla >= 1.0 else { return } lastTime = link.timestamp //刷新次數(shù) / 時(shí)間差 = 刷新頻次 fps = Double(count) / detla let fpsText = "\(String.init(format: "%.2f", fps)) FPS" count = 0 let attrMStr = NSMutableAttributedString(attributedString: NSAttributedString(string: fpsText)) if fps > 55.0 { //流暢 fpsColor = UIColor.green }else if (fps >= 50.0 && fps <= 55.0){ //一般 fpsColor = UIColor.yellow }else{ //卡頓 fpsColor = UIColor.red } attrMStr.setAttributes([NSAttributedString.Key.foregroundColor: fpsColor], range: NSMakeRange(0, attrMStr.length - 3)) attrMStr.setAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], range: NSMakeRange(attrMStr.length - 3, 3)) DispatchQueue.main.async { self.attributedText = attrMStr } }}2.主線程卡頓監(jiān)控我們可以添加Observer到主線程RunLoop中,通過(guò)監(jiān)聽(tīng)RunLoop狀態(tài)切換的耗時(shí),以達(dá)到監(jiān)控卡頓的目的實(shí)現(xiàn)思路:
在整個(gè)運(yùn)行循環(huán)中,需要監(jiān)聽(tīng)的主要就是RunLoop結(jié)束休眠到處理Source0的這段時(shí)間,如果時(shí)間過(guò)長(zhǎng),就證明有耗時(shí)操作
檢測(cè)主線程每次執(zhí)行消息循環(huán)的時(shí)間,當(dāng)這個(gè)時(shí)間大于規(guī)定的閾值時(shí),就記為發(fā)生了一次卡頓。這個(gè)也是微信卡頓三方matrix的原理
以下是一個(gè)簡(jiǎn)易版RunLoop監(jiān)控的實(shí)現(xiàn)
class LLBlockMonitor: NSObject { static let share = LLBlockMonitor.init() fileprivate var semaphore: DispatchSemaphore! fileprivate var timeoutCount: Int! fileprivate var activity: CFRunLoopActivity! private override init() { super.init() } public func start(){ //監(jiān)控兩個(gè)狀態(tài) registerObserver() //啟動(dòng)監(jiān)控 startMonitor() }}fileprivate extension LLBlockMonitor{ func registerObserver(){ let controllerPointer = Unmanaged<LLBlockMonitor>.passUnretained(self).toOpaque() var context: CFRunLoopObserverContext = CFRunLoopObserverContext(version: 0, info: controllerPointer, retain: nil, release: nil, copyDescription: nil) let observer: CFRunLoopObserver = CFRunLoopObserverCreate(nil, CFRunLoopActivity.allActivities.rawValue, true, 0, { (observer, activity, info) in guard info != nil else{ return } let monitor: LLBlockMonitor = Unmanaged<LLBlockMonitor>.fromOpaque(info!).takeUnretainedValue() monitor.activity = activity let sem: DispatchSemaphore = monitor.semaphore sem.signal() }, &context) CFRunLoopAddObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes) } func startMonitor(){ //創(chuàng)建信號(hào) semaphore = DispatchSemaphore(value: 0) //在子線程監(jiān)控時(shí)長(zhǎng) DispatchQueue.global().async { while(true){ // 超時(shí)時(shí)間是 1 秒,沒(méi)有等到信號(hào)量,st 就不等于 0, RunLoop 所有的任務(wù) let st = self.semaphore.wait(timeout: DispatchTime.now()+1.0) if st != DispatchTimeoutResult.success { //監(jiān)聽(tīng)兩種狀態(tài)kCFRunLoopBeforeSources 、kCFRunLoopAfterWaiting, if self.activity == CFRunLoopActivity.beforeSources || self.activity == CFRunLoopActivity.afterWaiting { self.timeoutCount += 1 if self.timeoutCount < 2 { print("timeOutCount = \(self.timeoutCount)") continue } // 一秒左右的衡量尺度 很大可能性連續(xù)來(lái) 避免大規(guī)模打印! print("檢測(cè)到超過(guò)兩次連續(xù)卡頓") } } self.timeoutCount = 0 } } }}使用時(shí),直接調(diào)用即可
LLBlockMonitor.share.start()也可以直接使用三方庫(kù)
Swift的卡頓檢測(cè)第三方ANREye,其主要思路是:創(chuàng)建子線程進(jìn)行循環(huán)監(jiān)測(cè),每次檢測(cè)時(shí)設(shè)置標(biāo)記置為true,然后派發(fā)任務(wù)到主線程,標(biāo)記置為false,接著子線程睡眠超過(guò)閾值時(shí),判斷標(biāo)記是否為false,如果沒(méi)有,說(shuō)明主線程發(fā)生了卡頓
OC可以使用微信matrix、滴滴DoraemonKit
iOS中的耗電優(yōu)化我們平時(shí)造成電量消耗的主要來(lái)源有哪些呢?一般造成耗電來(lái)源有以下這些
CPU的處理網(wǎng)絡(luò)的連接定位圖像的展示和處理耗電優(yōu)化的一些具體方案1.盡可能降低CPU、GPU功耗詳情參照上面關(guān)于卡頓優(yōu)化的相關(guān)處理
2.少用定時(shí)器定時(shí)器的使用也會(huì)造成一定的電量消耗,因?yàn)橐恢痹诔绦蛑斜O(jiān)聽(tīng)執(zhí)行
3.優(yōu)化I/O操作(文件的讀寫(xiě))盡量不要頻繁寫(xiě)入小數(shù)據(jù),最好批量一次性寫(xiě)入
讀寫(xiě)大量重要數(shù)據(jù)時(shí),考慮用dispatch_io,其提供了基于GCD的異步操作文件I/O的API。用dispatch_io系統(tǒng)會(huì)優(yōu)化磁盤(pán)訪問(wèn)
數(shù)據(jù)量比較大的,建議使用數(shù)據(jù)庫(kù)(比如SQLite、CoreData),數(shù)據(jù)庫(kù)內(nèi)部對(duì)讀寫(xiě)已經(jīng)做了相應(yīng)的優(yōu)化處理了#### 4.網(wǎng)絡(luò)優(yōu)化減少、壓縮網(wǎng)絡(luò)數(shù)據(jù),可以采用JSON和protobuf這樣格式相對(duì)較小的傳輸格式
如果多次請(qǐng)求的結(jié)果是相同的,盡量使用緩存,可以利用NSCache來(lái)進(jìn)行緩存使用斷點(diǎn)續(xù)傳,否則網(wǎng)絡(luò)不穩(wěn)定時(shí)可能多次傳輸相同的內(nèi)容
做好網(wǎng)絡(luò)狀態(tài)的監(jiān)控,網(wǎng)絡(luò)不可用時(shí),不要嘗試執(zhí)行網(wǎng)絡(luò)請(qǐng)求
讓用戶(hù)可以取消長(zhǎng)時(shí)間運(yùn)行或者速度很慢的網(wǎng)絡(luò)操作,設(shè)置合適的超時(shí)時(shí)間
批量傳輸,比如,下載視頻流時(shí),不要傳輸很小的數(shù)據(jù)包,直接下載整個(gè)文件或者一大塊一大塊地下載。如果下載廣告,一次性多下載一些,然后再慢慢展示。如果下載電子郵件,一次下載多封,不要一封一封地下載#### 5.定位優(yōu)化如果只是需要快速確定用戶(hù)位置,最好用CLLocationManager的requestLocation方法。定位完成后,會(huì)自動(dòng)讓定位硬件斷電如果不是導(dǎo)航應(yīng)用,盡量不要實(shí)時(shí)更新位置,定位完畢就關(guān)掉定位服務(wù)盡量降低定位精度,比如盡量不要使用精度最高的kCLLocationAccuracyBest需要后臺(tái)定位時(shí),盡量設(shè)置pausesLocationUpdatesAutomatically為YES,如果用戶(hù)不太可能移動(dòng)的時(shí)候系統(tǒng)會(huì)自動(dòng)暫停位置更新盡量不要使用startMonitoringSignificantLocationChanges,優(yōu)先考慮startMonitoringForRegion:#### 6.硬件檢測(cè)優(yōu)化用戶(hù)移動(dòng)、搖晃、傾斜設(shè)備時(shí),會(huì)產(chǎn)生動(dòng)作(motion)事件,這些事件由加速度計(jì)、陀螺儀、磁力計(jì)等硬件檢測(cè)。在不需要檢測(cè)的場(chǎng)合,應(yīng)該及時(shí)關(guān)閉這些硬件## APP的啟動(dòng)優(yōu)化
APP的啟動(dòng)我們先來(lái)了解一下APP的啟動(dòng)有哪幾種
APP的啟動(dòng)可以分為2種- 冷啟動(dòng)(Cold Launch):從零開(kāi)始啟動(dòng)APP- 熱啟動(dòng)(Warm Launch):APP已經(jīng)在內(nèi)存中,在后臺(tái)存活著,再次點(diǎn)擊圖標(biāo)啟動(dòng)APP
我們對(duì)APP啟動(dòng)時(shí)間的優(yōu)化,主要是針對(duì)冷啟動(dòng)進(jìn)行優(yōu)化
通過(guò)Xcode打印分析啟動(dòng)過(guò)程我們可以通過(guò)Xcode添加環(huán)境變量可以打印出APP的啟動(dòng)時(shí)間分析
1.找到路徑Edit scheme -> Run -> Arguments -> Environment Variables
2.添加DYLD_PRINT_STATISTICS,設(shè)置為1
3.然后運(yùn)行程序,可以看到控制臺(tái)的打印如下
如果需要更詳細(xì)的信息,那就添加DYLD_PRINT_STATISTICS_DETAILS設(shè)置為1
然后查看控制臺(tái)的打印如下
上述操作也僅僅是作為一個(gè)參考,如果啟動(dòng)時(shí)間小于400ms,那就屬于正常范圍,如果超出該值,就需要考慮一定的啟動(dòng)優(yōu)化了
APP的啟動(dòng)過(guò)程APP的冷啟動(dòng)可以概括為3大階段
dyld- Runtime- maindylddyld(dynamic link editor),Apple的動(dòng)態(tài)鏈接器,可以用來(lái)裝載Mach-O文件(可執(zhí)行文件、動(dòng)態(tài)庫(kù)等)啟動(dòng)APP時(shí),dyld所做的事情如下
啟動(dòng)APP時(shí),dyld會(huì)先裝載APP的可執(zhí)行文件,同時(shí)會(huì)遞歸加載所有依賴(lài)的動(dòng)態(tài)庫(kù)
當(dāng)dyld把可執(zhí)行文件、動(dòng)態(tài)庫(kù)都裝載完畢后,會(huì)通知Runtime進(jìn)行下一步的處理
Runtime啟動(dòng)APP時(shí),Runtime所做的事情如下
Runtime會(huì)調(diào)用map_images進(jìn)行可執(zhí)行文件內(nèi)容的解析和處理
進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊(cè)O(shè)bjc類(lèi) 、初始化類(lèi)對(duì)象等等)
在load_images中調(diào)用call_load_methods,調(diào)用所有Class和Category的+load方法
調(diào)用C++靜態(tài)初始化器和__attribute__((constructor))修飾的函數(shù)
到此為止,可執(zhí)行文件和動(dòng)態(tài)庫(kù)中所有的符號(hào)(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被Runtime所管理
main整個(gè)啟動(dòng)過(guò)程可以概述為:
APP的啟動(dòng)由dyld主導(dǎo),將可執(zhí)行文件加載到內(nèi)存,順便加載所有依賴(lài)的動(dòng)態(tài)庫(kù)
并由Runtime負(fù)責(zé)加載成objc定義的結(jié)構(gòu)
然后所有初始化工作結(jié)束后,dyld就會(huì)調(diào)用main函數(shù)
接下來(lái)就是UIApplicationMain函數(shù),AppDelegate的application:didFinishLaunchingWithOptions:方法
APP的啟動(dòng)優(yōu)化方案dyld階段減少動(dòng)態(tài)庫(kù)、合并一些動(dòng)態(tài)庫(kù)(定期清理不必要的動(dòng)態(tài)庫(kù))
減少Objc類(lèi)、分類(lèi)的數(shù)量、減少Selector數(shù)量(定期清理不必要的類(lèi)、分類(lèi))
減少C++虛函數(shù)數(shù)量(C++一旦有虛函數(shù),就會(huì)多維護(hù)一張?zhí)摫恚?/p>
Swift盡量使用struct
runtime加載階段用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++靜態(tài)構(gòu)造器、ObjC的+load#### 執(zhí)行main函數(shù)階段在不影響用戶(hù)體驗(yàn)的前提下,盡可能將一些操作延遲,不要全部都放在finishLaunching方法中按需加載## 安裝包瘦身我們開(kāi)發(fā)的安裝包(IPA)主要由可執(zhí)行文件、資源組成
在我們?nèi)粘i_(kāi)發(fā)中,項(xiàng)目業(yè)務(wù)會(huì)越來(lái)越多,慢慢就會(huì)積攢下一些不必要的代碼和資源,我們可以對(duì)其進(jìn)行一定的瘦身優(yōu)化
資源(圖片、音頻、視頻等)我們?cè)谑褂庙?xiàng)目里的資源時(shí),盡量采取無(wú)損壓縮的,會(huì)適當(dāng)減少包的大小
當(dāng)項(xiàng)目里的資源太多了,我們可以通過(guò)一些工具來(lái)清除無(wú)用的或者重復(fù)的資源
可執(zhí)行文件瘦身我們可以對(duì)編譯器做一定的優(yōu)化Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default這幾個(gè)配置都改為YES
去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions設(shè)置為NO, Other C Flags添加-fno-exceptions
我們可以利用一些工具對(duì)沒(méi)有使用的代碼進(jìn)行清除1.例如AppCode,檢測(cè)未使用的代碼:菜單欄 -> Code -> Inspect Code
2.編寫(xiě)LLVM插件檢測(cè)出重復(fù)代碼、未被調(diào)用的代碼
3.通過(guò)生成Link Map文件,可以查看可執(zhí)行文件的具體組成和大小分析
將Link Map File的路徑改成我們桌面路徑,然后將Write Link Map File改成Yes
然后會(huì)生成這么一個(gè)文件
由于其文件內(nèi)容過(guò)于龐大,不利于我們分析,可借助第三方工具解析LinkMap文件:https://github.com/huanxsd/LinkMap
掃描二維碼推送至手機(jī)訪問(wèn)。
版權(quán)聲明:本文由信途科技轉(zhuǎn)載于網(wǎng)絡(luò),如有侵權(quán)聯(lián)系站長(zhǎng)刪除。
轉(zhuǎn)載請(qǐng)注明出處http://macbookprostickers.com/xintu/57699.html