1.dSYM你是如何分析的?
2.多線程有哪幾種?你更傾向于哪一種惭缰?
3.單例弊端?
4.如何把異步線程轉(zhuǎn)換成同步任務(wù)進(jìn)行單元測(cè)試笼才?
5.介紹下App啟動(dòng)的完成過(guò)程漱受?
6.比如App啟動(dòng)過(guò)慢,你可能想到的因素有哪些骡送?
7.0x8badf00d表示是什么昂羡?
8.怎么防止反編譯?
9.說(shuō)說(shuō)你遇到到的技術(shù)難點(diǎn)摔踱?
10.說(shuō)說(shuō)你了解的第三方原理或底層知識(shí)虐先?
1.dSYM你是如何分析的?
什么是 dSYM 文件
Xcode編譯項(xiàng)目后派敷,我們會(huì)看到一個(gè)同名的 dSYM 文件蛹批,dSYM 是保存 16 進(jìn)制函數(shù)地址映射信息的中轉(zhuǎn)文件,我們調(diào)試的 symbols 都會(huì)包含在這個(gè)文件中篮愉,并且每次編譯項(xiàng)目的時(shí)候都會(huì)生成一個(gè)新的 dSYM 文件腐芍,位于 /Users/<用戶名>/Library/Developer/Xcode/Archives 目錄下,對(duì)于每一個(gè)發(fā)布版本我們都很有必要保存對(duì)應(yīng)的 Archives 文件 ( AUTOMATICALLY SAVE THE DSYM FILES 這篇文章介紹了通過(guò)腳本每次編譯后都自動(dòng)保存 dSYM 文件)试躏。
dSYM 文件有什么作用
當(dāng)我們軟件 release 模式打包或上線后猪勇,不會(huì)像我們?cè)?Xcode 中那樣直觀的看到用崩潰的錯(cuò)誤,這個(gè)時(shí)候我們就需要分析 crash report 文件了颠蕴,iOS 設(shè)備中會(huì)有日志文件保存我們每個(gè)應(yīng)用出錯(cuò)的函數(shù)內(nèi)存地址泣刹,通過(guò) Xcode 的 Organizer 可以將 iOS 設(shè)備中的 DeviceLog 導(dǎo)出成 crash 文件,這個(gè)時(shí)候我們就可以通過(guò)出錯(cuò)的函數(shù)地址去查詢 dSYM 文件中程序?qū)?yīng)的函數(shù)名和文件名裁替。大前提是我們需要有軟件版本對(duì)應(yīng)的 dSYM 文件项玛,這也是為什么我們很有必要保存每個(gè)發(fā)布版本的 Archives 文件了貌笨。
如何將文件一一對(duì)應(yīng)
每一個(gè) xx.app 和 xx.app.dSYM 文件都有對(duì)應(yīng)的 UUID弱判,crash 文件也有自己的 UUID,只要這三個(gè)文件的 UUID 一致锥惋,我們就可以通過(guò)他們解析出正確的錯(cuò)誤函數(shù)信息了昌腰。
1.查看 xx.app 文件的 UUID,terminal 中輸入命令 :
dwarfdump --uuid xx.app/xx (xx代表你的項(xiàng)目名)
2.查看 xx.app.dSYM 文件的 UUID 膀跌,在 terminal 中輸入命令:
dwarfdump --uuid xx.app.dSYM
3.crash 文件內(nèi)第一行 Incident Identifier 就是該 crash 文件的 UUID遭商。
dSYM工具
于是我抽了幾個(gè)小時(shí)的時(shí)間將這些命令封裝到一個(gè)應(yīng)用中,也為以后解決bug提供了便利捅伤。
使用步驟:
1.將打包發(fā)布軟件時(shí)的xcarchive文件拖入軟件窗口內(nèi)的任意位置(支持多個(gè)文件同時(shí)拖入劫流,注意:文件名不要包含空格)
2.選中任意一個(gè)版本的xcarchive文件,右邊會(huì)列出該xcarchive文件支持的CPU類型,選中錯(cuò)誤對(duì)應(yīng)的CPU類型祠汇。
3.對(duì)比錯(cuò)誤給出的UUID和工具界面中給出的UUID是否一致仍秤。
4.將錯(cuò)誤地址輸入工具的文本框中,點(diǎn)擊分析可很。
2.多線程有哪幾種诗力?你更傾向于哪一種?
第一種:pthread
.特點(diǎn):
1)一套通用的多線程API
2)適用于Unix\Linux\Windows等系統(tǒng)
3)跨平臺(tái)\可移植
4)使用難度大
b.使用語(yǔ)言:c語(yǔ)言
c.使用頻率:幾乎不用
d.線程生命周期:由程序員進(jìn)行管理
第二種:NSThread
a.特點(diǎn):
1)使用更加面向?qū)ο?/p>
2)簡(jiǎn)單易用我抠,可直接操作線程對(duì)象
b.使用語(yǔ)言:OC語(yǔ)言
c.使用頻率:偶爾使用
d.線程生命周期:由程序員進(jìn)行管理
第三種:GCD
a.特點(diǎn):
1)旨在替代NSThread等線程技術(shù)
2)充分利用設(shè)備的多核(自動(dòng))
b.使用語(yǔ)言:C語(yǔ)言
c.使用頻率:經(jīng)常使用
d.線程生命周期:自動(dòng)管理
第四種:NSOperation
a.特點(diǎn):
1)基于GCD(底層是GCD)
2)比GCD多了一些更簡(jiǎn)單實(shí)用的功能
3)使用更加面向?qū)ο?/p>
b.使用語(yǔ)言:OC語(yǔ)言
c.使用頻率:經(jīng)常使用
d.線程生命周期:自動(dòng)管理
多線程的原理
同一時(shí)間苇本,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)
多線程并發(fā)(同時(shí))執(zhí)行菜拓,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換)
如果CPU調(diào)度線程的時(shí)間足夠快瓣窄,就造成了多線程并發(fā)執(zhí)行的假象
思考:如果線程非常非常多,會(huì)發(fā)生什么情況纳鼎?
CPU會(huì)在N多線程之間調(diào)度康栈,CPU會(huì)累死,消耗大量的CPU資源
每條線程被調(diào)度執(zhí)行的頻次會(huì)降低(線程的執(zhí)行效率降低)
多線程的優(yōu)點(diǎn)
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)提高資源利用率(CPU喷橙、內(nèi)存利用率)
多線程的缺點(diǎn)
開(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下啥么,主線程占用1M,子線程占用512KB)贰逾,如果開(kāi)啟大量的線程悬荣,會(huì)占用大量的內(nèi)存空間,降低程序的性能
線程越多疙剑,CPU在調(diào)度線程上的開(kāi)銷就越大
程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信氯迂、多線程的數(shù)據(jù)共享
你更傾向于哪一種?
傾向于GCD:
GCD 技術(shù)是一個(gè)輕量的言缤,底層實(shí)現(xiàn)隱藏的神奇技術(shù)嚼蚀,我們能夠通過(guò)GCD和block輕松實(shí)現(xiàn)多線程編程,有時(shí)候管挟,GCD相比其他系統(tǒng)提供的多線程方法更加有效轿曙,當(dāng)然,有時(shí)候GCD不是最佳選擇僻孝,另一個(gè)多線程編程的技術(shù) NSOprationQueue 讓我們能夠?qū)⒑笈_(tái)線程以隊(duì)列方式依序執(zhí)行导帝,并提供更多操作的入口,這和 GCD 的實(shí)現(xiàn)有些類似穿铆。
這種類似不是一個(gè)巧合您单,在早期,MacOX 與 iOS 的程序都普遍采用Operation Queue來(lái)進(jìn)行編寫(xiě)后臺(tái)線程代碼荞雏,而之后出現(xiàn)的GCD技術(shù)大體是依照前者的原則來(lái)實(shí)現(xiàn)的虐秦,而隨著GCD的普及平酿,在iOS 4 與 MacOS X 10.6以后,Operation Queue的底層實(shí)現(xiàn)都是用GCD來(lái)實(shí)現(xiàn)的悦陋。
那這兩者直接有什么區(qū)別呢染服?
1.?? ?GCD是底層的C語(yǔ)言構(gòu)成的API,而NSOperationQueue及相關(guān)對(duì)象是Objc的對(duì)象叨恨。在GCD中柳刮,在隊(duì)列中執(zhí)行的是由block構(gòu)成的任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu)痒钝;而Operation作為一個(gè)對(duì)象秉颗,為我們提供了更多的選擇;
2.?? ?在NSOperationQueue中送矩,我們可以隨時(shí)取消已經(jīng)設(shè)定要準(zhǔn)備執(zhí)行的任務(wù)(當(dāng)然蚕甥,已經(jīng)開(kāi)始的任務(wù)就無(wú)法阻止了),而GCD沒(méi)法停止已經(jīng)加入queue的block(其實(shí)是有的栋荸,但需要許多復(fù)雜的代碼)菇怀;
3.?? ?NSOperation能夠方便地設(shè)置依賴關(guān)系,我們可以讓一個(gè)Operation依賴于另一個(gè)Operation晌块,這樣的話盡管兩個(gè)Operation處于同一個(gè)并行隊(duì)列中爱沟,但前者會(huì)直到后者執(zhí)行完畢后再執(zhí)行;
4.?? ?我們能將KVO應(yīng)用在NSOperation中匆背,可以監(jiān)聽(tīng)一個(gè)Operation是否完成或取消呼伸,這樣子能比GCD更加有效地掌控我們執(zhí)行的后臺(tái)任務(wù);
5.?? ?在NSOperation中钝尸,我們能夠設(shè)置NSOperation的priority優(yōu)先級(jí)括享,能夠使同一個(gè)并行隊(duì)列中的任務(wù)區(qū)分先后地執(zhí)行,而在GCD中珍促,我們只能區(qū)分不同任務(wù)隊(duì)列的優(yōu)先級(jí)铃辖,如果要區(qū)分block任務(wù)的優(yōu)先級(jí),也需要大量的復(fù)雜代碼猪叙;
6.?? ?我們能夠?qū)SOperation進(jìn)行繼承娇斩,在這之上添加成員變量與成員方法,提高整個(gè)代碼的復(fù)用度沐悦,這比簡(jiǎn)單地將block任務(wù)排入執(zhí)行隊(duì)列更有自由度成洗,能夠在其之上添加更多自定制的功能。
總的來(lái)說(shuō)藏否,Operation queue 提供了更多你在編寫(xiě)多線程程序時(shí)需要的功能公壤,并隱藏了許多線程調(diào)度呐赡,線程取消與線程優(yōu)先級(jí)的復(fù)雜代碼,為我們提供簡(jiǎn)單的API入口堰燎。從編程原則來(lái)說(shuō),一般我們需要盡可能的使用高等級(jí)淆储、封裝完美的API冠场,在必須時(shí)才使用底層API。但是我認(rèn)為當(dāng)我們的需求能夠以更簡(jiǎn)單的底層代碼完成的時(shí)候本砰,簡(jiǎn)潔的GCD或許是個(gè)更好的選擇碴裙,而Operation queue 為我們提供能更多的選擇。
傾向于:NSOperation
NSOperation相對(duì)于GCD:
1点额,NSOperation擁有更多的函數(shù)可用舔株,具體查看api。NSOperationQueue 是在GCD基礎(chǔ)上實(shí)現(xiàn)的还棱,只不過(guò)是GCD更高一層的抽象载慈。
2,在NSOperationQueue中珍手,可以建立各個(gè)NSOperation之間的依賴關(guān)系办铡。
3,NSOperationQueue支持KVO琳要」丫撸可以監(jiān)測(cè)operation是否正在執(zhí)行(isExecuted)、是否結(jié)束(isFinished)稚补,是否取消(isCanceld)
4晒杈,GCD 只支持FIFO 的隊(duì)列,而NSOperationQueue可以調(diào)整隊(duì)列的執(zhí)行順序(通過(guò)調(diào)整權(quán)重)孔厉。NSOperationQueue可以方便的管理并發(fā)拯钻、NSOperation之間的優(yōu)先級(jí)。
使用NSOperation的情況:各個(gè)操作之間有依賴關(guān)系撰豺、操作需要取消暫停粪般、并發(fā)管理、控制操作之間優(yōu)先級(jí)污桦,限制同時(shí)能執(zhí)行的線程數(shù)量.讓線程在某時(shí)刻停止/繼續(xù)等亩歹。
使用GCD的情況:一般的需求很簡(jiǎn)單的多線程操作,用GCD都可以了凡橱,簡(jiǎn)單高效小作。
從編程原則來(lái)說(shuō),一般我們需要盡可能的使用高等級(jí)稼钩、封裝完美的API顾稀,在必須時(shí)才使用底層API。
當(dāng)需求簡(jiǎn)單坝撑,簡(jiǎn)潔的GCD或許是個(gè)更好的選擇静秆,而Operation queue 為我們提供能更多的選擇粮揉。
3.單例弊端?
優(yōu)點(diǎn):
1:一個(gè)類只被實(shí)例化一次抚笔,提供了對(duì)唯一實(shí)例的受控訪問(wèn)扶认。
2:節(jié)省系統(tǒng)資源
3:允許可變數(shù)目的實(shí)例。
缺點(diǎn):
1:一個(gè)類只有一個(gè)對(duì)象殊橙,可能造成責(zé)任過(guò)重辐宾,在一定程度上違背了“單一職責(zé)原則”。
2:由于單利模式中沒(méi)有抽象層膨蛮,因此單例類的擴(kuò)展有很大的困難叠纹。
3:濫用單例將帶來(lái)一些負(fù)面問(wèn)題,如為了節(jié)省資源將數(shù)據(jù)庫(kù)連接池對(duì)象設(shè)計(jì)為的單例類鸽疾,可能會(huì)導(dǎo)致共享連接池對(duì)象的程序過(guò)多而出現(xiàn)連接池溢出吊洼;如果實(shí)例化的對(duì)象長(zhǎng)時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為是垃圾而被回收制肮,這將導(dǎo)致對(duì)象狀態(tài)的丟失冒窍。
4.如何把異步線程轉(zhuǎn)換成同步任務(wù)進(jìn)行單元測(cè)試?
在項(xiàng)目中的應(yīng)用:強(qiáng)制把異步任務(wù)轉(zhuǎn)換為同步任務(wù)來(lái)方便進(jìn)行單元測(cè)試
下面是 Parse 的一段代碼:
@interface PFEventuallyQueueTestHelper : NSObject {
dispatch_semaphore_t events[PFEventuallyQueueEventCount];
}
- (void)clear;
- (void)notify:(PFEventuallyQueueTestHelperEvent)event;
- (BOOL)waitFor:(PFEventuallyQueueTestHelperEvent)event;
注釋是這樣寫(xiě)的:
PFEventuallyQueueTestHelper gets notifications of various events happening in the command cache, // so that tests can be synchronized. See CommandTests.m for examples of how to use this.
強(qiáng)制把異步任務(wù)轉(zhuǎn)換為同步任務(wù)來(lái)方便進(jìn)行單元測(cè)試豺鼻。這個(gè)用途信號(hào)量是最合適的用途综液。但注意并不推薦應(yīng)用到除此之外的其它場(chǎng)景!
這種異步轉(zhuǎn)同步便于單元測(cè)試的用法類似于下面的寫(xiě)法:
#define WAIT_FOREVER [self waitForStatus:XCTAsyncTestCaseStatusSucceeded timeout:DBL_MAX];
#define NOTIFY [self notify:XCTAsyncTestCaseStatusSucceeded];
- (void)testInstallationMutated {
NSDictionary *dict = [self jsonWithFileName:@"TestInstallationSave"];
AVInstallation *installation = [AVInstallation currentInstallation];
[installation objectFromDictionary:dict];
[installation setObject:@(YES) forKey:@"enableNoDisturb"];
[installation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
XCTAssertNil(error);
NOTIFY;
}];
WAIT;
}
信號(hào)量屬性底層工具儒飒,他雖然非常強(qiáng)大谬莹,但在多數(shù)需要使用它的場(chǎng)合,最好從設(shè)計(jì)角度重新考慮桩了,看是否可以不用附帽,應(yīng)該優(yōu)先考慮使用諸如操作隊(duì)列這樣的高級(jí)工具。通尘可以通過(guò)增加一個(gè)分派隊(duì)列配合 dispatch_suspend 蕉扮,或者通過(guò)其它方式分解操作來(lái)避免使用信號(hào)量。信號(hào)量并非不好颗圣,只是它本身是鎖喳钟,能不使用就不用。盡量用 cocoa 框架中的高級(jí)抽象在岂,信號(hào)量非常接近底層奔则。所以除了上面的例子是最佳應(yīng)用場(chǎng)景外,不推薦應(yīng)用到除此之外的其它場(chǎng)景蔽午!
《關(guān)于dispatch_semaphore的使用》 中有這樣的描述:
關(guān)于信號(hào)量易茬,一般可以用停車來(lái)比喻。
停車場(chǎng)剩余4個(gè)車位祠丝,那么即使同時(shí)來(lái)了四輛車也能停的下疾呻。如果此時(shí)來(lái)了五輛車除嘹,那么就有一輛需要等待写半。
信號(hào)量的值就相當(dāng)于剩余車位的數(shù)目岸蜗,dispatch_semaphore_wait函數(shù)就相當(dāng)于來(lái)了一輛車,
dispatch_semaphore_signal叠蝇,就相當(dāng)于走了一輛車璃岳。停車位的剩余數(shù)目在初始化的時(shí)候就已經(jīng)指明了(dispatch_semaphore_create(long value))
調(diào)用一次dispatch_semaphore_signal,剩余的車位就增加一個(gè)悔捶;調(diào)用一次dispatch_semaphore_wait剩余車位就減少一個(gè)铃慷;
當(dāng)剩余車位為0時(shí),再來(lái)車(即調(diào)用dispatch_semaphore_wait)就只能等待蜕该。有可能同時(shí)有幾輛車等待一個(gè)停車位犁柜。有些車主
沒(méi)有耐心,給自己設(shè)定了一段等待時(shí)間堂淡,這段時(shí)間內(nèi)等不到停車位就走了馋缅,如果等到了就開(kāi)進(jìn)去停車。而有些車主就像把車停在這绢淀,
所以就一直等下去萤悴。
《GCD dispatch_semaphore 信號(hào)量 協(xié)調(diào)線程同步》也有類似的比喻:
以一個(gè)停車場(chǎng)是運(yùn)作為例。為了簡(jiǎn)單起見(jiàn)皆的,假設(shè)停車場(chǎng)只有三個(gè)車位覆履,一開(kāi)始三個(gè)車位都是空的。這時(shí)如果同時(shí)來(lái)了五輛車费薄,看門(mén)人允許其中三輛不受阻礙的進(jìn)入硝全,然后放下車攔,剩下的車則必須在入口等待楞抡,此后來(lái)的車也都不得不在入口處等待伟众。這時(shí),有一輛車離開(kāi)停車場(chǎng)拌倍,看門(mén)人得知后赂鲤,打開(kāi)車攔,放入一輛柱恤,如果又離開(kāi)兩輛数初,則又可以放入兩輛,如此往復(fù)梗顺。
在這個(gè)停車場(chǎng)系統(tǒng)中泡孩,車位是公共資源,每輛車好比一個(gè)線程寺谤,看門(mén)人起的就是信號(hào)量的作用仑鸥。 更進(jìn)一步吮播,信號(hào)量的特性如下:信號(hào)量是一個(gè)非負(fù)整數(shù)(車位數(shù)),所有通過(guò)它的線程(車輛)都會(huì)將該整數(shù)減一(通過(guò)它當(dāng)然是為了使用資源)眼俊,當(dāng)該整數(shù)值為零時(shí)意狠,所有試圖通過(guò)它的線程都將處于等待狀態(tài)。在信號(hào)量上我們定義兩種操作: Wait(等待) 和 Release(釋放)疮胖。 當(dāng)一個(gè)線程調(diào)用Wait(等待)操作時(shí)环戈,它要么通過(guò)然后將信號(hào)量減一,要么一直等下去澎灸,直到信號(hào)量大于一或超時(shí)院塞。Release(釋放)實(shí)際上是在信號(hào)量上執(zhí)行加操作,對(duì)應(yīng)于車輛離開(kāi)停車場(chǎng)性昭,該操作之所以叫做“釋放”是因?yàn)榧硬僮鲗?shí)際上是釋放了由信號(hào)量守護(hù)的資源拦止。
這個(gè)比喻里可以用一個(gè)表格來(lái)表示:
5.介紹下App啟動(dòng)的完成過(guò)程?
1. App啟動(dòng)過(guò)程
??? ?解析Info.plist
??? ?加載相關(guān)信息糜颠,例如如閃屏
??? ?沙箱建立汹族、權(quán)限檢查
??? ?Mach-O加載
??? ?如果是胖二進(jìn)制文件,尋找合適當(dāng)前CPU類別的部分
??? ?加載所有依賴的Mach-O文件(遞歸調(diào)用Mach-O加載的方法)
??? ?定位內(nèi)部括蝠、外部指針引用鞠抑,例如字符串、函數(shù)等
??? ?執(zhí)行聲明為_(kāi)_attribute__((constructor))的C函數(shù)
??? ?加載類擴(kuò)展(Category)中的方法
??? ?C++靜態(tài)對(duì)象加載忌警、調(diào)用ObjC的 +load 函數(shù)
??? ?程序執(zhí)行
·?? ?1.main函數(shù)
·?? ?2.執(zhí)行UIApplicationMain函數(shù)
·?? ? 1.創(chuàng)建UIApplication對(duì)象
·?? ? 2.創(chuàng)建UIApplicationDelegate對(duì)象并復(fù)制
·?? ? 3.讀取配置文件info.plist搁拙,設(shè)置程序啟動(dòng)的一些屬性,(關(guān)于info.plist的內(nèi)容可網(wǎng)上搜索下)
·?? ? 4.創(chuàng)建應(yīng)用程序的Main Runloop循環(huán)
·?? ?3.UIApplicationDelegate對(duì)象開(kāi)始處理監(jiān)聽(tīng)到的事件
·?? ? 1.程序啟動(dòng)成功之后法绵,首先調(diào)用application:didFinishLaunchingWithOptions:方法,
·?? ? 如果info.plist文件中配置了啟動(dòng)storyboard文件名箕速,則加載storyboard文件。
·?? ? 如果沒(méi)有配置朋譬,則根據(jù)代碼來(lái)創(chuàng)建UIWindow--->UIWindow的rootViewController-->顯示
6.比如App啟動(dòng)過(guò)慢盐茎,你可能想到的因素有哪些?
1. 影響啟動(dòng)性能的因素
App啟動(dòng)過(guò)程中每一個(gè)步驟都會(huì)影響啟動(dòng)性能徙赢,但是有些部分所消耗的時(shí)間少之又少字柠,另外有些部分根本無(wú)法避免,考慮到投入產(chǎn)出比狡赐,我們只列出我們可以優(yōu)化的部分:
main()函數(shù)之前耗時(shí)的影響因素
??? ?動(dòng)態(tài)庫(kù)加載越多窑业,啟動(dòng)越慢。
??? ?ObjC類越多枕屉,啟動(dòng)越慢
??? ?C的constructor函數(shù)越多常柄,啟動(dòng)越慢
??? ?C++靜態(tài)對(duì)象越多,啟動(dòng)越慢
??? ?ObjC的+load越多,啟動(dòng)越慢
實(shí)驗(yàn)證明西潘,在ObjC類的數(shù)目一樣多的情況下卷玉,需要加載的動(dòng)態(tài)庫(kù)越多,App啟動(dòng)就越慢喷市。同樣的相种,在動(dòng)態(tài)庫(kù)一樣多的情況下东抹,ObjC的類越多蚂子,App的啟動(dòng)也越慢沃测。需要加載的動(dòng)態(tài)庫(kù)從1個(gè)上升到10個(gè)的時(shí)候缭黔,用戶幾乎感知不到任何分別,但從10個(gè)上升到100個(gè)的時(shí)候就會(huì)變得十分明顯蒂破。同理馏谨,100個(gè)類和1000個(gè)類,可能也很難查察覺(jué)得出附迷,但1000個(gè)類和10000個(gè)類的分別就開(kāi)始明顯起來(lái)惧互。
同樣的,盡量不要寫(xiě)__attribute__((constructor))的C函數(shù)喇伯,也盡量不要用到C++的靜態(tài)對(duì)象喊儡;至于ObjC的+load方法,似乎大家已經(jīng)習(xí)慣不用它了稻据。任何情況下艾猜,能用dispatch_once()來(lái)完成的,就盡量不要用到以上的方法捻悯。
main()函數(shù)之后耗時(shí)的影響因素
??? ?執(zhí)行main()函數(shù)的耗時(shí)
??? ?執(zhí)行applicationWillFinishLaunching的耗時(shí)
??? ?rootViewController及其childViewController的加載匆赃、view及其subviews的加載
applicationWillFinishLaunching的耗時(shí)
如果有這樣這樣的代碼:
答案是:
1.?? ?-[MQQTabBarController viewDidLoad]
2.?? ?-[MQQTab1ViewController viewDidLoad]
3.?? ?-[AppDelegate application:didFinishLaunchingWithOptions:]
4.?? ?-[MQQTab2ViewController viewDidLoad] (點(diǎn)擊了第二個(gè)tab之后加載)
5.?? ?-[MQQTab3ViewController viewDidLoad] (點(diǎn)擊了第三個(gè)tab之后加載)
一般而言,大部分情況下我們都會(huì)把界面的初始化過(guò)程放在viewDidLoad今缚,但是這個(gè)過(guò)程會(huì)影響消耗啟動(dòng)的時(shí)間算柳。特別是在類似TabBarController這種會(huì)嵌套childViewController的ViewController的情況,它也會(huì)把部分children也初始化姓言,因此各種viewDidLoad會(huì)遞歸的進(jìn)行瞬项。
最簡(jiǎn)單的解決的方法,是把viewController延后加載何荚,但實(shí)際上這屬于一種掩耳盜鈴囱淋,確實(shí),applicationWillFinishLaunching的耗時(shí)是降下來(lái)了兽泣,但用戶體驗(yàn)上并沒(méi)有感覺(jué)變快绎橘。
更好一點(diǎn)的解決方法有點(diǎn)類似facebook,主視圖會(huì)第一時(shí)間加載,但里面的數(shù)據(jù)和界面都會(huì)延后加載称鳞,這樣用戶就會(huì)階段性的獲得視覺(jué)上的變化涮较,從而在視覺(jué)體驗(yàn)上感覺(jué)App啟動(dòng)得很快。
【第二部分】?jī)?yōu)化的目標(biāo)
由于每個(gè)App的情況有所不同冈止,需要加載的數(shù)據(jù)量也有所不同狂票,事實(shí)上我們無(wú)法使用一種統(tǒng)一的標(biāo)準(zhǔn)來(lái)衡量不同的App。蘋(píng)果熙暴。
??? ?應(yīng)該在400ms內(nèi)完成main()函數(shù)之前的加載
??? ?整體過(guò)程耗時(shí)不能超過(guò)20秒闺属,否則系統(tǒng)會(huì)kill掉進(jìn)程,App啟動(dòng)失敗
400ms內(nèi)完成main()函數(shù)前的加載的建議值是怎樣定出來(lái)的呢周霉?其實(shí)我也沒(méi)有太深究過(guò)這個(gè)問(wèn)題掂器,但是,當(dāng)用戶點(diǎn)擊了一個(gè)App的圖標(biāo)時(shí)俱箱,iOS做動(dòng)畫(huà)到閃屏圖出現(xiàn)的時(shí)長(zhǎng)正好是這個(gè)數(shù)字国瓮,我想也許跟這個(gè)有關(guān)。
針對(duì)不同規(guī)模的App狞谱,我們的目標(biāo)應(yīng)該有所取舍乃摹。例如,對(duì)于像手機(jī)QQ這種集整個(gè)SNG的代碼大成擼出來(lái)的App跟衅,對(duì)動(dòng)態(tài)庫(kù)的使用在所難免孵睬,但對(duì)于WiFi管家,由于在用戶連接WiFi的時(shí)候需要非沉骢危快速的響應(yīng)掰读,所以快速啟動(dòng)就非常重要。
那么撩穿,如何定制優(yōu)化的目標(biāo)呢磷支?首先,要確定啟動(dòng)性能的界限食寡,例如雾狈,在各種App性能的指標(biāo)中,哪一此屬于啟動(dòng)性能的范疇抵皱,哪一些則于App的流暢度性能善榛?我認(rèn)為應(yīng)該首先把啟動(dòng)過(guò)程分為四個(gè)部分:
1.?? ?main()函數(shù)之前
2.?? ?main()函數(shù)之后至applicationWillFinishLaunching完成
3.?? ?App完成所有本地?cái)?shù)據(jù)的加載并將相應(yīng)的信息展示給用戶
4.?? ?App完成所有聯(lián)網(wǎng)數(shù)據(jù)的加載并將相應(yīng)的信息展示給用戶
1+2一起決定了我們需要用戶等待多久才能出現(xiàn)一個(gè)主視圖,同時(shí)也是技術(shù)上可以精確測(cè)量的時(shí)長(zhǎng)呻畸,1+2+3決定了用戶視覺(jué)上的等待出現(xiàn)有用信息所需要的時(shí)長(zhǎng)移盆,1+2+3+4決定了我們需要多少時(shí)間才能讓我們需要展示給用戶的所有信息全部出現(xiàn)。
淘寶的iOS客戶端無(wú)疑是各部分都做得非常優(yōu)秀的典型伤为。它所承載的業(yè)務(wù)完全不比微信和手機(jī)QQ少咒循,但幾乎瞬間完成了啟動(dòng)据途,并利用緩存機(jī)制使得用戶馬上看到“貌似完整”的界面,然后立即又刷新了剛剛聯(lián)網(wǎng)更新回來(lái)的信息叙甸。也就是說(shuō)颖医,無(wú)論是技術(shù)上還是視覺(jué)上,它都非常的“快”裆蒸。
1. 移除不需要用到的動(dòng)態(tài)庫(kù)
因?yàn)閃iFi管家是個(gè)小項(xiàng)目熔萧,用到的動(dòng)態(tài)庫(kù)不多,自動(dòng)化處理的優(yōu)勢(shì)不大僚祷,我這里也就簡(jiǎn)單的把依賴的動(dòng)態(tài)移除出項(xiàng)目佛致,再根據(jù)編譯錯(cuò)誤一個(gè)一個(gè)加回來(lái)。如果有靠譜的方法辙谜,歡迎大家補(bǔ)充一下俺榆。
2. 移除不需要用到的類
項(xiàng)目做久了總有一些吊詭的類像幽靈一樣驅(qū)之不去,由于【不要相信產(chǎn)品經(jīng)理】的思想作怪筷弦,需求變更后肋演,有些類可能用不上了,但卻因?yàn)閾?dān)心需求再變回來(lái)就沒(méi)有移除掉烂琴,后來(lái)就徹底忘記要移除了。
為了解決這個(gè)歷史問(wèn)題蜕乡,在這個(gè)過(guò)程中我試過(guò)多種方法來(lái)掃描沒(méi)有用到的類奸绷,其中有一種是編譯后對(duì)ObjC類的指針引用進(jìn)行反向掃描,可惜實(shí)際上收獲不是很明顯层玲,而且還要寫(xiě)很多例外代碼來(lái)處理一些特殊情況号醉。后來(lái)發(fā)現(xiàn)一個(gè)叫做fui(Find Unused Imports)的開(kāi)源項(xiàng)目能很好的分析出不再使用的類,準(zhǔn)確率非常高辛块,唯一的問(wèn)題是它處理不了動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)里提供的類畔派,也處理不了C++的類模板。
使用方法是在Terminal中cd到項(xiàng)目所在的目錄润绵,然后執(zhí)行fui find线椰,然后等上那么幾分鐘(是的你沒(méi)有看錯(cuò),真的需要好幾分鐘甚至需要更長(zhǎng)的時(shí)間)尘盼,就可以得到一個(gè)列表了憨愉。由于這個(gè)工具還不是100%靠譜,可根據(jù)這個(gè)列表卿捎,在Xcode中手動(dòng)檢查并刪除不再用到的類配紫。
實(shí)際上,日常對(duì)代碼工程的維護(hù)非常重要午阵,如果制定好一套半廢棄代碼的維護(hù)方法躺孝,小問(wèn)題就不會(huì)積累成大問(wèn)題。有時(shí)候?qū)τ谝恍簳r(shí)不再使用的代碼,我也很糾結(jié)于要不要svn rm植袍,因?yàn)閺拇a歷史中找刪除掉的文件還是不太方便伪很。不知道大家有沒(méi)有相關(guān)的經(jīng)驗(yàn)可以分享,也請(qǐng)不吝賜教奋单。
3. 合并功能類似的類和擴(kuò)展(Category)
由于Category的實(shí)現(xiàn)原理锉试,和ObjC的動(dòng)態(tài)綁定有很強(qiáng)的關(guān)系,所以實(shí)際上類的擴(kuò)展是比較占用啟動(dòng)時(shí)間的览濒。盡量合并一些擴(kuò)展呆盖,會(huì)對(duì)啟動(dòng)有一定的優(yōu)化作用。不過(guò)個(gè)人認(rèn)為也不能因?yàn)樗加脝?dòng)時(shí)間而去逃避使用擴(kuò)展贷笛,畢竟程序員的時(shí)間比CPU的時(shí)間值錢应又,這里只是強(qiáng)調(diào)要合并一些在工程、架構(gòu)上沒(méi)有太大意義的擴(kuò)展乏苦。
4. 壓縮資源圖片
壓縮圖片為什么能加快啟動(dòng)速度呢株扛?因?yàn)閱?dòng)的時(shí)候大大小小的圖片加載個(gè)十來(lái)二十個(gè)是很正常的,圖片小了汇荐,IO操作量就小了洞就,啟動(dòng)當(dāng)然就會(huì)快了。
事實(shí)上掀淘,Xcode在編譯App的時(shí)候旬蟋,已經(jīng)自動(dòng)把需要打包到App里的資源圖片壓縮過(guò)一遍了。然而Xcode的壓縮會(huì)相對(duì)比較保守革娄。另一方面倾贰,我們正常的設(shè)計(jì)師由于需要符合其正常的審美需要生成的正常的PNG圖片,因此圖片大小是比較大的拦惋,然而如果以程序員的直男審美而采用過(guò)激的壓縮會(huì)直接激怒設(shè)計(jì)師匆浙。
解決各種矛盾的方法就是要找出一種相當(dāng)靠譜的壓縮方法,而且最好是基本無(wú)損的厕妖,而且壓縮率還要特別高首尼,至少要比Xcode自動(dòng)壓縮的效果要更好才有意義。經(jīng)過(guò)各種試驗(yàn)叹放,最后發(fā)現(xiàn)唯一可靠的壓縮算法是TinyPNG饰恕,其它各種方法,要么沒(méi)效果井仰,要么產(chǎn)生色差或模糊埋嵌。但是非常可惜的是TinyPNG并不是完全免費(fèi)的俱恶,而且需要通過(guò)網(wǎng)絡(luò)請(qǐng)求來(lái)壓縮圖片(應(yīng)該是為了保護(hù)其牛逼的壓縮算法)雹嗦。
為了解決這個(gè)問(wèn)題范舀,我寫(xiě)了一個(gè)類來(lái)執(zhí)行這個(gè)請(qǐng)求,請(qǐng)參見(jiàn)閱讀原文里的SSTinyPNGRequest和SSPNGCompressor了罪。因?yàn)檫@個(gè)項(xiàng)目只有我一個(gè)人在用所以代碼寫(xiě)得有點(diǎn)隨意锭环,有問(wèn)題可以私聊也可以在評(píng)論里問(wèn),有改進(jìn)的方法也非常歡迎指正泊藕。另外說(shuō)明一下辅辩,使用這個(gè)類需要你自行到?https://tinypng.com/developers?申請(qǐng)APIKey,每一個(gè)用戶每月有500張圖片壓縮是免費(fèi)的娃圆,而每個(gè)郵箱可以注冊(cè)一個(gè)用戶玫锋,你懂的。
5. 優(yōu)化applicationWillFinishLaunching
隨著項(xiàng)目做的時(shí)間長(zhǎng)了讼呢,applicationWillFinishLaunching里要處理的代碼會(huì)越積越多撩鹿,WiFi管家的iOS版本有一段時(shí)間沒(méi)有控制好,里面的邏輯亂得有點(diǎn)丟人悦屏。因?yàn)榭赡苌婕暗揭恍╉?xiàng)目的安全性問(wèn)題节沦,這里不能分享所有的優(yōu)化細(xì)節(jié)及發(fā)現(xiàn)的思路。僅列出在applicationWillFinishLaunching中主要需要處理的業(yè)務(wù)及相關(guān)問(wèn)題的改進(jìn)方案础爬。
這里大部分都是一些苦逼活甫贯,但有一點(diǎn)特別值得分享的是,有一些優(yōu)化幕帆,是無(wú)法在數(shù)據(jù)上體現(xiàn)的获搏,但是視覺(jué)上卻能給用戶較大的提升。例如在【各種業(yè)務(wù)請(qǐng)求配置更新】的部分失乾,經(jīng)過(guò)分析優(yōu)化后,啟動(dòng)過(guò)程并發(fā)的http請(qǐng)求數(shù)量從66條壓縮到了23條纬乍,如此一來(lái)為啟動(dòng)成功后新聞資訊及其圖片的加載留出了更多的帶寬碱茁,從而保證了在第一時(shí)間完成新聞資訊的加載。實(shí)際測(cè)試表明仿贬,光做KPI的事情是不夠的纽竣,人還是需要有點(diǎn)理想,經(jīng)過(guò)優(yōu)化茧泪,在視覺(jué)體驗(yàn)上進(jìn)步非常明顯蜓氨。
另外,過(guò)程中請(qǐng)教過(guò)SNG的大牛們队伟,聽(tīng)說(shuō)他們因?yàn)樾枰赼pplicationWillFinishLaunching里處理的業(yè)務(wù)更多穴吹,所以還做了管理器管理這些任務(wù),不過(guò)因?yàn)閃iFi管家是個(gè)小項(xiàng)目嗜侮,有點(diǎn)殺雞用牛刀的感覺(jué)港令,因此沒(méi)有深入研究啥容。
6. 優(yōu)化rootViewController加載
考慮到我作為一只程序猴,工資還行顷霹,為了給公司節(jié)約成本咪惠,在優(yōu)化之前,當(dāng)然需要先測(cè)試一下哪些ViewController的加載耗時(shí)比較大淋淀,然后再深入到具體業(yè)務(wù)中看哪些部分存在較大的優(yōu)化空間遥昧。同時(shí),先做優(yōu)化效果明顯的部分也有利于增強(qiáng)自己的信心朵纷。
在開(kāi)始講述問(wèn)題之前炭臭,我們先來(lái)看一下wife管家的UI層次結(jié)構(gòu):
一個(gè)看似簡(jiǎn)單的界面由于承載了很多業(yè)務(wù)需求,代碼量其實(shí)已經(jīng)非常驚人柴罐。這里我不具體講述這些驚人的業(yè)務(wù)量了徽缚,抽象而言可WiFi管家的UI架構(gòu)總體而言基于TabBarController的框架,三個(gè)tab分別是“連接”革屠、“發(fā)現(xiàn)”及“我的”凿试。App啟動(dòng)的時(shí)候,根據(jù)加載原理似芝,會(huì)加載TabBarController那婉、第一個(gè)Tab(“連接”)的ViewController及其所有childViewController。
UI構(gòu)架請(qǐng)看如下示意圖党瓮,其中藍(lán)色的部分需要在App啟動(dòng)的時(shí)候立即加載:
一個(gè)看似簡(jiǎn)單的界面由于承載了很多業(yè)務(wù)需求详炬,代碼量其實(shí)已經(jīng)非常驚人。這里我不具體講述這些驚人的業(yè)務(wù)量了寞奸,抽象而言可WiFi管家的UI架構(gòu)總體而言基于TabBarController的框架呛谜,三個(gè)tab分別是“連接”、“發(fā)現(xiàn)”及“我的”枪萄。App啟動(dòng)的時(shí)候隐岛,根據(jù)加載原理,會(huì)加載TabBarController瓷翻、第一個(gè)Tab(“連接”)的ViewController及其所有childViewController聚凹。
對(duì)所有啟動(dòng)相關(guān)的模塊打錨點(diǎn)計(jì)算耗時(shí)后,發(fā)現(xiàn)tabBarController和connectingViewController分別占用了applicationWillFinishLaunching耗時(shí)的31%和24%齐帚。加載耗費(fèi)了大量時(shí)間妒牙,這跟它所需要承載的邏輯任務(wù)似乎并不對(duì)稱。于是檢查相關(guān)代碼進(jìn)行深入分析对妄,發(fā)現(xiàn)了幾個(gè)問(wèn)題比較嚴(yán)重:
1.?? ?有些程序員可能架構(gòu)意識(shí)不是太強(qiáng)湘今,直接在tabBarController的啟動(dòng)過(guò)程中插入了各種奇怪的業(yè)務(wù),例如檢查WiFi連接狀態(tài)變化、配置拉取脆霎,而這些業(yè)務(wù)顯然應(yīng)該在另外的某些地方統(tǒng)一處理威沫,而不應(yīng)該在一個(gè)ViewController上纲菌。
2.?? ?由于一些歷史原因遣铝,連接頁(yè)的視圖控制器connectingViewController包含了三個(gè)childViewController:WiFiViewController阔蛉、3GViewController隙袁、errorViewController削咆,分別在WiFi狀態(tài)蚪拦、3G狀態(tài)和出錯(cuò)狀態(tài)下展示界面(三選一杖剪,其中一個(gè)展示的時(shí)候其它兩個(gè)視圖會(huì)隱藏)。
3.?? ?大部分view都是直接加載完的驰贷。有些界面的加載非常復(fù)雜盛嘿,比如再進(jìn)入App時(shí)會(huì)展示一個(gè)檢查WiFi可用性和安全性的動(dòng)畫(huà),由于需要疊加較多圖片括袒,這部分視圖的加載耗時(shí)較多次兆。
由于隨著幾次改版之后,連接頁(yè)的UI架構(gòu)已經(jīng)變得很不合理锹锰,歷史包袱還是比較重的芥炭,而且耦合比較嚴(yán)重,幾乎無(wú)法改動(dòng)恃慧,因此決定重構(gòu)园蝠。至于tabBarController,檢查代碼后決定簡(jiǎn)單的把不相關(guān)的業(yè)務(wù)做一些遷移痢士,優(yōu)化childViewController的加載過(guò)程彪薛,不作重構(gòu)。
由于本篇主要講啟動(dòng)性能優(yōu)化怠蹂,重構(gòu)涉及的軟件工程和設(shè)計(jì)模式方面的東西就不詳細(xì)論述了善延,對(duì)啟動(dòng)優(yōu)化的過(guò)程,主要是使用了更合理的分層結(jié)構(gòu)城侧,使得啟動(dòng)得以在更短的時(shí)間內(nèi)完成挚冤。
至此,WiFi管家的啟動(dòng)性能基本優(yōu)化完畢赞庶。
7. 挖掘最后一點(diǎn)性能優(yōu)化
由于WiFi管家是一個(gè)具有WiFi連接能力的App,因此有可能在后臺(tái)過(guò)程中完成冷啟動(dòng)過(guò)程(實(shí)際上是在用戶進(jìn)入系統(tǒng)的WiFi設(shè)置時(shí)澳骤,iOS會(huì)啟動(dòng)WiFi管家歧强,以便請(qǐng)求WiFi密碼)。在這種情況下为肮,整個(gè)rootViewController都是不需要加載的摊册。
【第四部分】總結(jié)
??? ?利用DYLD_PRINT_STATISTICS分析main()函數(shù)之前的耗時(shí)
??? ?重新梳理架構(gòu),減少動(dòng)態(tài)庫(kù)颊艳、ObjC類的數(shù)目茅特,減少Category的數(shù)目
??? ?定期掃描不再使用的動(dòng)態(tài)庫(kù)忘分、類、函數(shù)白修,例如每?jī)蓚€(gè)迭代一次
??? ?用dispatchonce()代替所有的__attribute__((constructor))函數(shù)妒峦、C++靜態(tài)對(duì)象初始化、ObjC的+load
??? ?在設(shè)計(jì)師可接受的范圍內(nèi)壓縮圖片的大小兵睛,會(huì)有意外收獲
??? ?利用錨點(diǎn)分析applicationWillFinishLaunching的耗時(shí)
??? ?將不需要馬上在applicationWillFinishLaunching執(zhí)行的代碼延后執(zhí)行
??? ?rootViewController的加載肯骇,適當(dāng)將某一級(jí)的childViewController或subviews延后加載
??? ?如果你的App可能會(huì)被后臺(tái)拉起并冷啟動(dòng),可考慮不加載rootViewController
??? ?不應(yīng)放過(guò)的一些小細(xì)節(jié)
??? ?異步操作并不影響指標(biāo)祖很,但有可能影響交互體驗(yàn)笛丙,例如大量網(wǎng)絡(luò)請(qǐng)求導(dǎo)致數(shù)據(jù)擁堵
??? ?有時(shí)候一些交互上的優(yōu)化比技術(shù)手段效果更明顯,視覺(jué)上的快決不是冰冷的數(shù)據(jù)可以解釋的假颇,好好和你們的設(shè)計(jì)師談?wù)剟?dòng)畫(huà)
7.0x8badf00d表示是什么胚鸯?
0x8badf00d: 讀做 “ate bad food”! (把數(shù)字換成字母,是不是很像 :p)該編碼表示應(yīng)用是因?yàn)榘l(fā)生watchdog超時(shí)而被iOS終止的笨鸡。??通常是應(yīng)用花費(fèi)太多時(shí)間而無(wú)法啟動(dòng)姜钳、終止或響應(yīng)用系統(tǒng)事件。
0xbad22222: 該編碼表示 VoIP 應(yīng)用因?yàn)檫^(guò)于頻繁重啟而被終止镜豹。
0xdead10cc: 讀做 “dead lock”!該代碼表明應(yīng)用因?yàn)樵诤笈_(tái)運(yùn)行時(shí)占用系統(tǒng)資源傲须,如通訊錄數(shù)據(jù)庫(kù)不釋放而被終止 。
0xdeadfa11: 讀做 “dead fall”! 該代碼表示應(yīng)用是被用戶強(qiáng)制退出的趟脂。根據(jù)蘋(píng)果文檔, 強(qiáng)制退出發(fā)生在用戶長(zhǎng)按開(kāi)關(guān)按鈕直到出現(xiàn) “滑動(dòng)來(lái)關(guān)機(jī)”, 然后長(zhǎng)按 Home按鈕泰讽。強(qiáng)制退出將產(chǎn)生 包含0xdeadfa11 異常編碼的崩潰日志, 因?yàn)榇蠖鄶?shù)是強(qiáng)制退出是因?yàn)閼?yīng)用阻塞了界面。
8.怎么防止反編譯昔期?
1.本地?cái)?shù)據(jù)加密
iOS應(yīng)用防反編譯加密技術(shù)之一:對(duì)NSUserDefaults已卸,sqlite存儲(chǔ)文件數(shù)據(jù)加密,保護(hù)帳號(hào)和關(guān)鍵信息
2.URL編碼加密
iOS應(yīng)用防反編譯加密技術(shù)之二:對(duì)程序中出現(xiàn)的URL進(jìn)行編碼加密硼一,防止URL被靜態(tài)分析
3.網(wǎng)絡(luò)傳輸數(shù)據(jù)加密
iOS應(yīng)用防反編譯加密技術(shù)之三:對(duì)客戶端傳輸數(shù)據(jù)提供加密方案累澡,有效防止通過(guò)網(wǎng)絡(luò)接口的攔截獲取數(shù)據(jù)
4.方法體,方法名高級(jí)混淆
iOS應(yīng)用防反編譯加密技術(shù)之四:對(duì)應(yīng)用程序的方法名和方法體進(jìn)行混淆般贼,保證源碼被逆向后無(wú)法解析代碼
5.程序結(jié)構(gòu)混排加密
iOS應(yīng)用防反編譯加密技術(shù)之五:對(duì)應(yīng)用程序邏輯結(jié)構(gòu)進(jìn)行打亂混排愧哟,保證源碼可讀性降到最低
6.借助第三方APP加固,例如:網(wǎng)易云易盾
9.說(shuō)說(shuō)你遇到到的技術(shù)難點(diǎn)哼蛆?
你遇到的問(wèn)題難度蕊梧,一定程度決定了你的水平。如實(shí)反映就好腮介。只是一道承上啟下的問(wèn)題肥矢!
以下問(wèn)題是群友提供的:
問(wèn)題1.地理空間距離計(jì)算優(yōu)化
不管是“離我最近”還是“智能排序”,都涉及到計(jì)算用戶位置與各個(gè)團(tuán)購(gòu)單子或者商家的距離(注:在智能排序中距離作為一個(gè)重要的參數(shù)參與排序打分)叠洗。以篩選商家為例甘改,北京地區(qū)有5~6w個(gè)POI(本文將商家稱之為POI)旅东,當(dāng)用戶進(jìn)入商家頁(yè),請(qǐng)求北京全城+所有品類+離我最近/智能排序時(shí)十艾,我們篩選服務(wù)需要計(jì)算一遍用戶位置與北京全城所有POI的距離抵代。
這種大量計(jì)算距離的場(chǎng)景十分消耗資源,從測(cè)試來(lái)看目前5w個(gè)點(diǎn)僅計(jì)算一遍距離就需要7ms疟羹,而到100w的時(shí)候就需要140多ms主守,隨著數(shù)據(jù)的快速增長(zhǎng),篩選服務(wù)的性能將會(huì)非抽冢堪憂参淫。
如何優(yōu)化距離的計(jì)算,進(jìn)而提高計(jì)算速度愧杯、降低cpu使用率已經(jīng)迫在眉睫涎才。移動(dòng)后臺(tái)團(tuán)購(gòu)組在此方向上進(jìn)行了些許探索,下文將分為4部分展開(kāi):1)地理空間距離計(jì)算原理力九;2)Lucene使用的距離計(jì)算公式耍铜;3)優(yōu)化方案;4)實(shí)際應(yīng)用跌前。
2 地理空間距離計(jì)算原理
地理空間距離計(jì)算方法較多棕兼,目前我們使用的可以分為兩類:1)球面模型,這種模型將地球看成一個(gè)標(biāo)準(zhǔn)球體抵乓,球面上兩點(diǎn)之間的最短距離即大圓弧長(zhǎng)伴挚,這種方法使用較廣,在我們服務(wù)端被廣泛使用灾炭;2)橢球模型茎芋,該模型最貼近真實(shí)地球,精度也最高蜈出,但計(jì)算較為復(fù)雜田弥,目前客戶端有在使用,但實(shí)際上我們的應(yīng)用對(duì)精度的要求沒(méi)有那么高铡原。
下面著重介紹我們最常用的基于球面模型的地理空間距離計(jì)算公式偷厦,推導(dǎo)也只需要高中數(shù)學(xué)知識(shí)即可。
該模型將地球看成圓球燕刻,假設(shè)地球上有A(ja,wa)沪哺,B(jb,wb)兩點(diǎn)(注:ja和jb分別是A和B的經(jīng)度,wa和wb分別是A和B的緯度)酌儒,A和B兩點(diǎn)的球面距離就是AB的弧長(zhǎng),AB弧長(zhǎng)=R*角AOB(注:角AOB是A跟B的夾角枯途,O是地球的球心忌怎,R是地球半徑籍滴,約為6367000米)。如何求出角AOB呢榴啸?可以先求AOB的最大邊AB的長(zhǎng)度孽惰,再根據(jù)余弦定律可以求夾角。
如何求出AB長(zhǎng)度呢鸥印?
1)根據(jù)經(jīng)緯度勋功,以及地球半徑R,將A库说、B兩點(diǎn)的經(jīng)緯度坐標(biāo)轉(zhuǎn)換成球體三維坐標(biāo)狂鞋;
2)根據(jù)A、B兩點(diǎn)的三維坐標(biāo)求AB長(zhǎng)度;
3)根據(jù)余弦定理求出角AOB;
4)AB弧長(zhǎng)=R*角AOB.
#3 Lucene使用的地理空間距離算法
團(tuán)購(gòu)app后臺(tái)使用lucene來(lái)篩選團(tuán)購(gòu)單子和商家潜的,lucene使用了spatial4j工具包來(lái)計(jì)算地理空間距離骚揍,而spatial4j提供了多種基于球面模型的地理空間距離的公式,其中一種就是上面我們推導(dǎo)的公式啰挪,稱之為球面余弦公式信不。還有一種最常用的是Haversine公式,該公式是spatial4j計(jì)算距離的默認(rèn)公式亡呵,本質(zhì)上是球面余弦函數(shù)的一個(gè)變換抽活,之前球面余弦公式中有cos(jb-ja)項(xiàng),當(dāng)系統(tǒng)的浮點(diǎn)運(yùn)算精度不高時(shí)锰什,在求算較近的兩點(diǎn)間的距離時(shí)會(huì)有較大誤差下硕,Haversine方法進(jìn)行了某種變換消除了cos(jb-ja)項(xiàng),因此不存在短距離求算時(shí)對(duì)系統(tǒng)計(jì)算精度的過(guò)多顧慮的問(wèn)題歇由。
1)Haversine公式代碼
public static double distHaversineRAD(double lat1, double lon1, double lat2, double lon2) {
double hsinX = Math.sin((lon1 - lon2) * 0.5);
double hsinY = Math.sin((lat1 - lat2) * 0.5);
double h = hsinY * hsinY +
(Math.cos(lat1) * Math.cos(lat2) * hsinX * hsinX);
return 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)) * 6367000;
}
2)Haversine公式性能
目前北京地區(qū)在線服務(wù)有5w個(gè)POI卵牍,計(jì)算一遍距離需要7ms。現(xiàn)在數(shù)據(jù)增長(zhǎng)特別快沦泌,未來(lái)北京地區(qū)POI數(shù)目增大到100w時(shí)糊昙,我們篩選服務(wù)僅計(jì)算距離這一項(xiàng)就需要消耗144多ms,性能十分堪憂谢谦。(注:本文測(cè)試環(huán)境的處理器為2.9GHz Intel Core i7释牺,內(nèi)存為8GB 1600 MHz DDR3,操作系統(tǒng)為OS X10.8.3回挽,實(shí)驗(yàn)在單線程環(huán)境下運(yùn)行)
POI數(shù)目
耗時(shí)(ms)
5w
7
10w
14
100w
144
#4 優(yōu)化方案
通過(guò)抓棧我們發(fā)現(xiàn)消耗cpu較多的線程很多都在執(zhí)行距離公式中的三角函數(shù)例如atan2等没咙。因此我們的優(yōu)化方向很直接---消減甚至消除三角函數(shù)∏基于此方向我們嘗試了多種方法祭刚,下文將一一介紹。
##4.1 坐標(biāo)轉(zhuǎn)換方法
1)基本思路
之前POI保存的是經(jīng)緯度(lon,lat),我們的計(jì)算場(chǎng)景是計(jì)算用戶位置與所有篩選出來(lái)的poi的距離涡驮,這里會(huì)涉及到大量三角函數(shù)計(jì)算暗甥。一種優(yōu)化思路是POI數(shù)據(jù)不保存經(jīng)緯度而保存球面模型下的三維坐標(biāo)(x,y,z),映射方法如下:
x = Math.cos(lat)Math.cos(lon);
y = Math.cos(lat)Math.sin(lon);
z = Math.sin(lat);
那么當(dāng)我們求夾角AOB時(shí)捉捅,只需要做一次點(diǎn)乘操作撤防。比如求(lon1,lat1)和 (lon2,lat2)的夾角,只需要計(jì)算x1x2 + y1y2 + z1*z2棒口, 這樣避免了大量三角函數(shù)的計(jì)算寄月。
在得到夾角之后,還需要執(zhí)行arccos函數(shù)无牵,將其轉(zhuǎn)換成角度漾肮,AB弧長(zhǎng)=角AOB*R(R是地球半徑)。
此方法性能如下:
POI數(shù)目
耗時(shí)(ms)
5w
3
10w
8
100w
88
2)進(jìn)一步優(yōu)化
我們的業(yè)務(wù)場(chǎng)景是在一個(gè)城市范圍內(nèi)進(jìn)行距離計(jì)算合敦,因此夾角AOB往往會(huì)比較小初橘,這個(gè)時(shí)候sinAOB約等于AOB,因此我們可以將 Math.acos(cosAOB)R 改為Math.sqrt(1 - cosAOBcosAOB)*R充岛,從而完全避免使用三角函數(shù)保檐,優(yōu)化后性能如下。
POI數(shù)目
耗時(shí)(ms)
5w
0.2
10w
0.5
100w
4
##4.2 簡(jiǎn)化距離計(jì)算公式方法
1)基本思路
我們的業(yè)務(wù)場(chǎng)景僅僅是在一個(gè)城市范圍內(nèi)進(jìn)行距離計(jì)算崔梗,也就是說(shuō)兩個(gè)點(diǎn)之間的距離一般不會(huì)超過(guò)200多千米夜只。由于范圍小,可以認(rèn)為經(jīng)線和緯線是垂直的蒜魄,如圖所示扔亥,要求A(116.8,39,78)和B(116.9谈为,39.68)兩點(diǎn)的距離旅挤,我們可以先求出南北方向距離AM,然后求出東西方向距離BM伞鲫,最后求矩形對(duì)角線距離粘茄,即sqrt(AMAM + BMBM)。
南北方向AM = R緯度差Math.PI/180.0秕脓;
東西方向BM = R經(jīng)度差Cos<當(dāng)?shù)鼐暥葦?shù)* Math.PI/180.0>
這種方式僅僅需要計(jì)算一次cos函數(shù)柒瓣。
public static double distanceSimplify(double lat1, double lng1, double lat2, double lng2, double[] a) {
double dx = lng1 - lng2; // 經(jīng)度差值
double dy = lat1 - lat2; // 緯度差值
double b = (lat1 + lat2) / 2.0; // 平均緯度
double Lx = toRadians(dx) * 6367000.0* Math.cos(toRadians(b)); // 東西距離
double Ly = 6367000.0 * toRadians(dy); // 南北距離
return Math.sqrt(Lx * Lx + Ly * Ly);? // 用平面的矩形對(duì)角距離公式計(jì)算總距離
}
}
我們對(duì)這個(gè)方法的有效性和性能進(jìn)行驗(yàn)證。
1.1)有效性驗(yàn)證
我們首先檢驗(yàn)這種簡(jiǎn)化是否能滿足我們應(yīng)用的精度吠架,如果精度較差將不能用于實(shí)際生產(chǎn)環(huán)境芙贫。
我們的方法叫distanceSimplify,lucene的方法叫distHaversineRAD傍药。下表是在不同尺度下兩個(gè)方法的相差情況磺平。
測(cè)試點(diǎn)對(duì)
distanceSimplify(米)
distHaversineRAD(米)
差別(米)
(39.941魂仍,116.45)(39.94, 116.451)
140.0285167225230
140.02851671981400
0.0
(39.96褪秀, 116.45)(39.94蓄诽, 116.40)
4804.421262839180
4804.421153907680
0.0
(39.96, 116.45)(39.94媒吗, 117.30)
72444.81551882200
72444.54071519510
0.3
(39.26, 115.25)(41.04乙埃, 117.30)
263525.6167839860
263508.55921886700
17.1
可以看到兩者在百米闸英、千米尺度上幾乎沒(méi)有差別,在萬(wàn)米尺度上也僅有分米的差別介袜,此外由于我們的業(yè)務(wù)是在一個(gè)城市范圍內(nèi)進(jìn)行篩選排序甫何,所以我們選擇了北京左下角和右上角兩點(diǎn)進(jìn)行比較,兩點(diǎn)相距有260多千米遇伞,兩個(gè)方法差別17m辙喂。從精度上看該優(yōu)化方法能滿足我們應(yīng)用需求。
1.2)性能驗(yàn)證
POI數(shù)目
耗時(shí)(ms)
5w
0.5
10w
1.1
100w
11
2)進(jìn)一步優(yōu)化
我們看到這里計(jì)算了一次cos這一三角函數(shù)鸠珠,如果我們能消除此三角函數(shù)巍耗,那么將進(jìn)一步提高計(jì)算效率。
如何消除cos三角函數(shù)呢渐排?
采用多項(xiàng)式來(lái)擬合cos三角函數(shù)炬太,這樣不就可以將三角函數(shù)轉(zhuǎn)換為加減乘除了嘛!
首先決定多項(xiàng)式的最高次數(shù)驯耻,次數(shù)為1和2顯然都無(wú)法很好擬合cos函數(shù)亲族,那么我們選擇3先嘗試吧,注:最高次數(shù)不是越多越好可缚,次數(shù)越高會(huì)產(chǎn)生過(guò)擬合問(wèn)題霎迫。
使用org.apache.commons.math3這一數(shù)學(xué)工具包來(lái)進(jìn)行擬合。中國(guó)的緯度范圍在10~60之間帘靡,即我們將此區(qū)間離散成Length份作為我們的訓(xùn)練集知给。
public static double[] trainPolyFit(int degree, int Length){
PolynomialCurveFitter polynomialCurveFitter = PolynomialCurveFitter.create(degree);
double minLat = 10.0; //中國(guó)最低緯度
double maxLat = 60.0; //中國(guó)最高緯度
double interv = (maxLat - minLat) / (double)Length;
List weightedObservedPoints = new ArrayList();
for(int i = 0; i < Length; i++) {
WeightedObservedPoint weightedObservedPoint = new WeightedObservedPoint(1,? minLat + (double)i*interv, Math.cos(toRadians(x[i])));
weightedObservedPoints.add(weightedObservedPoint);
}
return polynomialCurveFitter.fit(weightedObservedPoints);
}
public static double distanceSimplifyMore(double lat1, double lng1, double lat2, double lng2, double[] a) {
//1) 計(jì)算三個(gè)參數(shù)
double dx = lng1 - lng2; // 經(jīng)度差值
double dy = lat1 - lat2; // 緯度差值
double b = (lat1 + lat2) / 2.0; // 平均緯度
//2) 計(jì)算東西方向距離和南北方向距離(單位:米),東西距離采用三階多項(xiàng)式
double Lx = (a[3] * b*b*b? + a[2]* b*b? +a[1] * b + a[0] ) * toRadians(dx) * 6367000.0; // 東西距離
double Ly = 6367000.0 * toRadians(dy); // 南北距離
//3) 用平面的矩形對(duì)角距離公式計(jì)算總距離
return Math.sqrt(Lx * Lx + Ly * Ly);
}
我們對(duì)此優(yōu)化方法進(jìn)行有效性和性能驗(yàn)證测柠。
2.1)有效性驗(yàn)證
我們的優(yōu)化方法叫distanceSimplifyMore炼鞠,lucene的方法叫distHaversineRAD,下表是在不同尺度下兩個(gè)方法的相差情況轰胁。
測(cè)試點(diǎn)對(duì)
distanceSimplifyMore(米)
distHaversineRAD(米)
差別(米)
(39.941谒主,116.45)(39.94, 116.451)
140.0242769266660
140.02851671981400
0.0
(39.96赃阀, 116.45)(39.94霎肯, 116.40)
4804.113098854450
4804.421153907680
0.3
(39.96擎颖, 116.45)(39.94, 117.30)
72438.90919479560
72444.54071519510
5.6
(39.26观游, 115.25)(41.04搂捧, 117.30)
263516.676171262
263508.55921886700
8.1
可以看到在百米尺度上兩者幾乎未有差別,在千米尺度上僅有分米的區(qū)別懂缕,在更高尺度上如72千米僅有5.6m米別允跑,在264千米也僅有8.1米區(qū)別,因此該優(yōu)化方法的精度能滿足我們的應(yīng)用需求搪柑。
2.2)性能驗(yàn)證
POI數(shù)目
耗時(shí)(ms)
5w
0.1
10w
0.3
100w
4
#5 實(shí)際應(yīng)用
坐標(biāo)轉(zhuǎn)換方法和簡(jiǎn)化距離公式方法性能都非常高聋丝,相比lucene使用的Haversine算法大大提高了計(jì)算效率,然而坐標(biāo)轉(zhuǎn)換方法存在一些缺點(diǎn):
a)坐標(biāo)轉(zhuǎn)換后的數(shù)據(jù)不能被直接用于空間索引工碾。lucene可以直接對(duì)經(jīng)緯度進(jìn)行g(shù)eohash空間索引弱睦,而通過(guò)空間轉(zhuǎn)換變成三維數(shù)據(jù)后不能直接使用。我們的應(yīng)用有附近范圍篩選功能(例如附近5km的團(tuán)購(gòu)單子)渊额,通過(guò)geohash空間索引可以提高范圍篩選的效率况木;
b)坐標(biāo)轉(zhuǎn)換方法增大內(nèi)存開(kāi)銷。我們會(huì)將坐標(biāo)寫(xiě)入倒排索引中旬迹,之前坐標(biāo)是2列(經(jīng)度和緯度)火惊,現(xiàn)在變成3列(x,y,z),在使用中我們往往會(huì)將這數(shù)據(jù)放入到cache中舱权,因此會(huì)增大內(nèi)存開(kāi)銷矗晃;
c)坐標(biāo)轉(zhuǎn)換方法增大建索引開(kāi)銷。此方法本質(zhì)上是將計(jì)算從查詢階段放至到索引階段宴倍,因此提高了建索引的開(kāi)銷张症。
基于上述原因我們?cè)趯?shí)際應(yīng)用中采用簡(jiǎn)化距離公式方法(通過(guò)三次多項(xiàng)式來(lái)擬合cos三角函數(shù)),此方法在團(tuán)購(gòu)篩選和商家篩選的距離排序鸵贬、智能排序中已經(jīng)開(kāi)始使用俗他,與之前相比,篩選團(tuán)購(gòu)時(shí)北京全城美食品類距離排序響應(yīng)時(shí)間從40ms下降為20ms阔逼。
問(wèn)題2.iOS應(yīng)用架構(gòu)兆衅,我的架構(gòu)設(shè)計(jì)?
可從以下4個(gè)方面回答:
iOS應(yīng)用架構(gòu)談 view層的組織和調(diào)用方案 嗜浮?
iOS應(yīng)用架構(gòu)談 網(wǎng)絡(luò)層設(shè)計(jì)方案 羡亩?
iOS應(yīng)用架構(gòu)談 動(dòng)態(tài)部署方案 ?
iOS應(yīng)用架構(gòu)談 本地持久化方案危融?
什么樣app的架構(gòu)叫好架構(gòu)畏铆?
代碼整齊,分類明確吉殃,沒(méi)有common辞居,沒(méi)有core
不用文檔楷怒,或很少文檔,就能讓業(yè)務(wù)方上手
思路和方法要統(tǒng)一瓦灶,盡量不要多元
沒(méi)有橫向依賴鸠删,萬(wàn)不得已不出現(xiàn)跨層訪問(wèn)
對(duì)業(yè)務(wù)方該限制的地方有限制,該靈活的地方要給業(yè)務(wù)方創(chuàng)造靈活實(shí)現(xiàn)的條件
易測(cè)試贼陶,易拓展
保持一定量的超前性
接口少刃泡,接口參數(shù)少
高性能
第一類:精簡(jiǎn)型應(yīng)用架構(gòu)
這類架構(gòu)的文章分析主要還是圍繞MVC展開(kāi),以蘋(píng)果自帶UIViewController優(yōu)劣為出發(fā)點(diǎn)碉怔,再結(jié)合主流的MVP捅僵,MVVM,MVCS等變種進(jìn)行分析演變眨层。這類的探討重點(diǎn)在于M,V上荡,C三類角色的定義以及之間的數(shù)據(jù)事件流向的規(guī)范趴樱。很多小型應(yīng)用所面臨的問(wèn)題及其架構(gòu)層面的解決方案都集中在這一類。
第二類:綜合型應(yīng)用架構(gòu)
對(duì)于用戶量級(jí)在千萬(wàn)級(jí)或以上的應(yīng)用來(lái)說(shuō)酪捡,MVC這一層面的思考已無(wú)法應(yīng)對(duì)業(yè)務(wù)瘋狂增長(zhǎng)所帶來(lái)的負(fù)擔(dān)叁征。這類應(yīng)用往往需要專業(yè)資深的架構(gòu)師出面進(jìn)行深層次的思考設(shè)計(jì),業(yè)內(nèi)不少大廠如淘寶逛薇,天貓捺疼,攜程等都做過(guò)一些分享。不過(guò)到了這一層級(jí)的戰(zhàn)斗永罚,不光考驗(yàn)架構(gòu)師的技術(shù)積累啤呼,更重要的是架構(gòu)師對(duì)于業(yè)務(wù)的整體理解。我姑且把這類架構(gòu)名之為:綜合型應(yīng)用架構(gòu)呢袱。綜合型應(yīng)用架構(gòu)一般不會(huì)提到MVC官扣,更多是在探討“層”與“模塊”的劃分和耦合。后面我會(huì)就幾個(gè)經(jīng)典樣本做下詳盡深入的分析羞福。
第三類:深度優(yōu)化的綜合型應(yīng)用架構(gòu)
綜合型應(yīng)用架構(gòu)是應(yīng)對(duì)大規(guī)模業(yè)務(wù)增長(zhǎng)的必經(jīng)之路惕蹄,一旦架構(gòu)成型,后期業(yè)務(wù)膨脹會(huì)不停的打磨架構(gòu)本身治专,產(chǎn)品本身對(duì)體驗(yàn)質(zhì)量的追求會(huì)要求架構(gòu)師和技術(shù)團(tuán)隊(duì)不停的優(yōu)化架構(gòu)細(xì)節(jié)卖陵。這種優(yōu)化可以分為兩塊,第一是組件或模塊劃分的粒度越來(lái)越細(xì)张峰,第二是組件模塊的深度優(yōu)化泪蔫,比如網(wǎng)絡(luò)層的深度優(yōu)化,sqlite優(yōu)化(多線程挟炬,F(xiàn)TS鸥滨,安全等)嗦哆,數(shù)據(jù)加密,HotFix婿滓,Hybrid等老速,一些開(kāi)源的第三方庫(kù)已不能滿足要求,需要團(tuán)隊(duì)自己重造輪子凸主。這一層面的架構(gòu)設(shè)計(jì)涉及面廣橘券,對(duì)架構(gòu)師,團(tuán)隊(duì)技術(shù)人員的技術(shù)深度和業(yè)務(wù)理解能力有較高要求卿吐,短短一篇技術(shù)文章往往只能走馬觀花的介紹個(gè)大概旁舰,每一次優(yōu)化幾乎都可以作為一個(gè)專題來(lái)講解。
第四類:組織型應(yīng)用架構(gòu)
這類架構(gòu)在第三類的基礎(chǔ)之上更進(jìn)了一步嗡官,除了關(guān)注系統(tǒng)層面的架構(gòu)設(shè)計(jì)之外箭窜,更對(duì)團(tuán)隊(duì)或部門(mén)之間協(xié)作方式,各系統(tǒng)模塊的演進(jìn)方式衍腥,產(chǎn)品發(fā)布流程等都做了規(guī)范磺樱。除去業(yè)務(wù)膨脹帶來(lái)的壓力,人員增長(zhǎng)婆咸,各團(tuán)隊(duì)協(xié)作依賴增強(qiáng)等都會(huì)對(duì)app的質(zhì)量竹捉,迭代速度產(chǎn)生影響,這些問(wèn)題也需要從架構(gòu)層面去解決尚骄。這類結(jié)合技術(shù)架構(gòu)和組織架構(gòu)的分享還比較少块差。
以上四種類型的架構(gòu)又可以看做一般App從簡(jiǎn)至繁,公司規(guī)模隨之增長(zhǎng)的演進(jìn)過(guò)程倔丈,技術(shù)圈絕大部分的架構(gòu)類分享文章都可以歸為上述四類憨闰。
對(duì)于什么是架構(gòu)的學(xué)術(shù)定義,似乎大家并不太在意乃沙,更關(guān)心的是如何解決自身項(xiàng)目當(dāng)下的問(wèn)題起趾。雖然在我看來(lái)第一類架構(gòu)更像是在討論設(shè)計(jì)模式,但這里面確實(shí)又有非常多的知識(shí)可以深入挖掘警儒,這里就把所有“解決應(yīng)用整體設(shè)計(jì)問(wèn)題”的討論都?xì)w類于架構(gòu)這一話題训裆。
值得一提的是,架構(gòu)師的視野和積累一般都受限于自己所經(jīng)歷項(xiàng)目及業(yè)務(wù)的規(guī)模蜀铲。如果有機(jī)會(huì)边琉,工程師還是應(yīng)該盡可能去BAT這類巨頭級(jí)公司歷練一下,知識(shí)深度和廣度的構(gòu)建絕非紙上可得记劝。
可從以下方面回答:(舉例說(shuō)明变姨,攜程開(kāi)發(fā)提供)
核心功能SDK化
通訊、定位厌丑、Hybrid渔呵、數(shù)據(jù)庫(kù)、登錄咏花、分享舍咖、基礎(chǔ)庫(kù)等
直接提供給其他BU獨(dú)立App使用
公用業(yè)務(wù)功能組件化
地圖瑰钮、日歷、城市智蝠、圖片饭寺、通訊錄等13個(gè)公共組件
減少各BU重復(fù)開(kāi)發(fā)工作量
性能數(shù)據(jù)指標(biāo)采集:
網(wǎng)絡(luò)性能:網(wǎng)絡(luò)服務(wù)成功率、平均耗時(shí)叫挟、耗時(shí)分布
定位:獲取經(jīng)緯度成功率艰匙、城市定位成功率
啟動(dòng)時(shí)間、內(nèi)存抹恳、流量等指標(biāo)
多種緯度:系統(tǒng)员凝、App版本、網(wǎng)絡(luò)狀況奋献、位置等
網(wǎng)絡(luò)優(yōu)化
使用TCP長(zhǎng)連接實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)
根據(jù)網(wǎng)絡(luò)狀況2G/3G/4G/WIFI進(jìn)行調(diào)優(yōu)參數(shù)
根據(jù)連接/讀/寫(xiě)不同階段使用重試機(jī)制
使用IP列表避免DNS解析失敗或者劫持
根據(jù)網(wǎng)絡(luò)延遲選擇服務(wù)端IP(使用Ping)
使用ProtocolBuffer+Gzip減少Payload
10.說(shuō)說(shuō)你了解的第三方原理或底層知識(shí)健霹?
Runtime、Runloop瓶蚂、block
SD原理糖埋、YYCache、GCD源碼分析窃这、JSPatch原理等瞳别!