前言:先自己嘗試去回答,回答不了再看參考答案羹唠,你才能學(xué)的更多奕枢!
阿里一面娄昆、二面均在以下群文件中,自行下載缝彬!
**********BAT iOS面試題分享群:2466454 (吹水勿擾)**********
1.dSYM你是如何分析的萌焰?
2.多線程有哪幾種?你更傾向于哪一種谷浅?
3.單例弊端扒俯?
4.如何把異步線程轉(zhuǎn)換成同步任務(wù)進(jìn)行單元測(cè)試?
5.介紹下App啟動(dòng)的完成過程一疯?
6.比如App啟動(dòng)過慢撼玄,你可能想到的因素有哪些?
7.0x8badf00d表示是什么墩邀?
8.怎么防止反編譯掌猛?
9.說說你遇到到的技術(shù)難點(diǎn)?
10.說說你了解的第三方原理或底層知識(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 這篇文章介紹了通過腳本每次編譯后都自動(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)存地址园细,通過 Xcode 的 Organizer 可以將 iOS 設(shè)備中的 DeviceLog 導(dǎo)出成 crash 文件,這個(gè)時(shí)候我們就可以通過出錯(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 一致,我們就可以通過他們解析出正確的錯(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ū)ο?br>
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ū)ο?br>
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)
開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下坯苹,主線程占用1M隆檀,子線程占用512KB),如果開啟大量的線程粹湃,會(huì)占用大量的內(nèi)存空間恐仑,降低程序的性能
線程越多,CPU在調(diào)度線程上的開銷就越大
程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
你更傾向于哪一種?
傾向于GCD:
GCD 技術(shù)是一個(gè)輕量的蔽午,底層實(shí)現(xiàn)隱藏的神奇技術(shù)涕滋,我們能夠通過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來進(jìn)行編寫后臺(tái)線程代碼喘帚,而之后出現(xiàn)的GCD技術(shù)大體是依照前者的原則來實(shí)現(xiàn)的畅姊,而隨著GCD的普及,在iOS 4 與 MacOS X 10.6以后吹由,Operation Queue的底層實(shí)現(xiàn)都是用GCD來實(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)開始的任務(wù)就無法阻止了)溺蕉,而GCD沒法停止已經(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)聽一個(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ì)列更有自由度猪贪,能夠在其之上添加更多自定制的功能跷敬。
總的來說,Operation queue 提供了更多你在編寫多線程程序時(shí)需要的功能热押,并隱藏了許多線程調(diào)度西傀,線程取消與線程優(yōu)先級(jí)的復(fù)雜代碼斤寇,為我們提供簡(jiǎn)單的API入口。從編程原則來說拥褂,一般我們需要盡可能的使用高等級(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)的缝驳,只不過是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í)行順序(通過調(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)單高效。
從編程原則來說羽圃,一般我們需要盡可能的使用高等級(jí)乾胶、封裝完美的API,在必須時(shí)才使用底層API。
當(dāng)需求簡(jiǎn)單识窿,簡(jiǎn)潔的GCD或許是個(gè)更好的選擇斩郎,而Operation queue 為我們提供能更多的選擇。
3.單例弊端喻频? 優(yōu)點(diǎn):
1:一個(gè)類只被實(shí)例化一次缩宜,提供了對(duì)唯一實(shí)例的受控訪問。
2:節(jié)省系統(tǒng)資源
3:允許可變數(shù)目的實(shí)例甥温。
缺點(diǎn):
1:一個(gè)類只有一個(gè)對(duì)象脓恕,可能造成責(zé)任過重,在一定程度上違背了“單一職責(zé)原則”窿侈。
2:由于單利模式中沒有抽象層炼幔,因此單例類的擴(kuò)展有很大的困難。
3:濫用單例將帶來一些負(fù)面問題史简,如為了節(jié)省資源將數(shù)據(jù)庫(kù)連接池對(duì)象設(shè)計(jì)為的單例類乃秀,可能會(huì)導(dǎo)致共享連接池對(duì)象的程序過多而出現(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ù)來方便進(jìn)行單元測(cè)試
下面是 Parse 的一段代碼:
@interface PFEventuallyQueueTestHelper : NSObject {
dispatch_semaphore_t events[PFEventuallyQueueEventCount];
}
- (void)clear;
- (void)notify:(PFEventuallyQueueTestHelperEvent)event;
- (BOOL)waitFor:(PFEventuallyQueueTestHelperEvent)event;
注釋是這樣寫的:
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ù)來方便進(jìn)行單元測(cè)試刀脏。這個(gè)用途信號(hào)量是最合適的用途。但注意并不推薦應(yīng)用到除此之外的其它場(chǎng)景超凳!
這種異步轉(zhuǎn)同步便于單元測(cè)試的用法類似于下面的寫法:
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í)工具创夜。通澈脊颍可以通過增加一個(gè)分派隊(duì)列配合 dispatch_suspend ,或者通過其它方式分解操作來避免使用信號(hào)量驰吓。信號(hào)量并非不好涧尿,只是它本身是鎖,能不使用就不用檬贰。盡量用 cocoa 框架中的高級(jí)抽象姑廉,信號(hào)量非常接近底層。所以除了上面的例子是最佳應(yīng)用場(chǎng)景外偎蘸,不推薦應(yīng)用到除此之外的其它場(chǎng)景庄蹋!
《關(guān)于dispatch_semaphore的使用》 中有這樣的描述:
關(guān)于信號(hào)量,一般可以用停車來比喻迷雪。
停車場(chǎng)剩余4個(gè)車位限书,那么即使同時(shí)來了四輛車也能停的下。如果此時(shí)來了五輛車章咧,那么就有一輛需要等待倦西。
信號(hào)量的值就相當(dāng)于剩余車位的數(shù)目,dispatch_semaphore_wait函數(shù)就相當(dāng)于來了一輛車赁严,
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í)程剥,再來車(即調(diào)用dispatch_semaphore_wait)就只能等待劝枣。有可能同時(shí)有幾輛車等待一個(gè)停車位。有些車主
沒有耐心织鲸,給自己設(shè)定了一段等待時(shí)間舔腾,這段時(shí)間內(nèi)等不到停車位就走了,如果等到了就開進(jìn)去停車搂擦。而有些車主就像把車停在這稳诚,
所以就一直等下去。
《GCD dispatch_semaphore 信號(hào)量 協(xié)調(diào)線程同步》 也有類似的比喻:
以一個(gè)停車場(chǎng)是運(yùn)作為例瀑踢。為了簡(jiǎn)單起見扳还,假設(shè)停車場(chǎng)只有三個(gè)車位,一開始三個(gè)車位都是空的橱夭。這時(shí)如果同時(shí)來了五輛車普办,看門人允許其中三輛不受阻礙的進(jìn)入,然后放下車攔徘钥,剩下的車則必須在入口等待衔蹲,此后來的車也都不得不在入口處等待。這時(shí)呈础,有一輛車離開停車場(chǎng)舆驶,看門人得知后,打開車攔而钞,放入一輛沙廉,如果又離開兩輛,則又可以放入兩輛臼节,如此往復(fù)撬陵。
在這個(gè)停車場(chǎng)系統(tǒng)中珊皿,車位是公共資源,每輛車好比一個(gè)線程巨税,看門人起的就是信號(hào)量的作用蟋定。 更進(jìn)一步,信號(hào)量的特性如下:信號(hào)量是一個(gè)非負(fù)整數(shù)(車位數(shù))草添,所有通過它的線程(車輛)都會(huì)將該整數(shù)減一(通過它當(dāng)然是為了使用資源)驶兜,當(dāng)該整數(shù)值為零時(shí),所有試圖通過它的線程都將處于等待狀態(tài)远寸。在信號(hào)量上我們定義兩種操作: Wait(等待) 和 Release(釋放)抄淑。 當(dāng)一個(gè)線程調(diào)用Wait(等待)操作時(shí),它要么通過然后將信號(hào)量減一驰后,要么一直等下去肆资,直到信號(hào)量大于一或超時(shí)。Release(釋放)實(shí)際上是在信號(hào)量上執(zhí)行加操作灶芝,對(duì)應(yīng)于車輛離開停車場(chǎng)迅耘,該操作之所以叫做“釋放”是因?yàn)榧硬僮鲗?shí)際上是釋放了由信號(hào)量守護(hù)的資源。
這個(gè)比喻里可以用一個(gè)表格來表示:
5.介紹下App啟動(dòng)的完成過程监署? 1. App啟動(dòng)過程
? 解析Info.plist
? 加載相關(guān)信息颤专,例如如閃屏
? 沙箱建立、權(quán)限檢查
**? Mach-O加載**
? 如果是胖二進(jìn)制文件钠乏,尋找合適當(dāng)前CPU類別的部分
? 加載所有依賴的Mach-O文件(遞歸調(diào)用Mach-O加載的方法)
? 定位內(nèi)部栖秕、外部指針引用恨闪,例如字符串譬正、函數(shù)等
? 執(zhí)行聲明為__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ì)象開始處理監(jiān)聽到的事件
· 1.程序啟動(dòng)成功之后暑塑,首先調(diào)用application:didFinishLaunchingWithOptions:方法,
· 如果info.plist文件中配置了啟動(dòng)storyboard文件名,則加載storyboard文件锅必。
· 如果沒有配置事格,則根據(jù)代碼來創(chuàng)建UIWindow--->UIWindow的rootViewController-->顯示
6.比如App啟動(dòng)過慢,你可能想到的因素有哪些搞隐? 1. 影響啟動(dòng)性能的因素
App啟動(dòng)過程中每一個(gè)步驟都會(huì)影響啟動(dòng)性能驹愚,但是有些部分所消耗的時(shí)間少之又少,另外有些部分根本無法避免劣纲,考慮到投入產(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è)類,可能也很難查察覺得出橘荠,但1000個(gè)類和10000個(gè)類的分別就開始明顯起來屿附。
同樣的,盡量不要寫attribute((constructor))的C函數(shù)哥童,也盡量不要用到C++的靜態(tài)對(duì)象挺份;至于ObjC的+load方法,似乎大家已經(jīng)習(xí)慣不用它了贮懈。任何情況下匀泊,能用dispatch_once()來完成的,就盡量不要用到以上的方法朵你。
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ì)把界面的初始化過程放在viewDidLoad抡医,但是這個(gè)過程會(huì)影響消耗啟動(dòng)的時(shí)間躲因。特別是在類似TabBarController這種會(huì)嵌套childViewController的ViewController的情況,它也會(huì)把部分children也初始化忌傻,因此各種viewDidLoad會(huì)遞歸的進(jìn)行毛仪。
最簡(jiǎn)單的解決的方法,是把viewController延后加載芯勘,但實(shí)際上這屬于一種掩耳盜鈴箱靴,確實(shí),applicationWillFinishLaunching的耗時(shí)是降下來了荷愕,但用戶體驗(yàn)上并沒有感覺變快衡怀。
更好一點(diǎn)的解決方法有點(diǎn)類似facebook棍矛,主視圖會(huì)第一時(shí)間加載,但里面的數(shù)據(jù)和界面都會(huì)延后加載抛杨,這樣用戶就會(huì)階段性的獲得視覺上的變化够委,從而在視覺體驗(yàn)上感覺App啟動(dòng)得很快。
【第二部分】?jī)?yōu)化的目標(biāo)
由于每個(gè)App的情況有所不同怖现,需要加載的數(shù)據(jù)量也有所不同茁帽,事實(shí)上我們無法使用一種統(tǒng)一的標(biāo)準(zhǔn)來衡量不同的App。蘋果屈嗤。
? 應(yīng)該在400ms內(nèi)完成main()函數(shù)之前的加載
? 整體過程耗時(shí)不能超過20秒潘拨,否則系統(tǒng)會(huì)kill掉進(jìn)程,App啟動(dòng)失敗
400ms內(nèi)完成main()函數(shù)前的加載的建議值是怎樣定出來的呢饶号?其實(shí)我也沒有太深究過這個(gè)問題铁追,但是,當(dāng)用戶點(diǎn)擊了一個(gè)App的圖標(biāo)時(shí)茫船,iOS做動(dòng)畫到閃屏圖出現(xiàn)的時(shí)長(zhǎng)正好是這個(gè)數(shù)字琅束,我想也許跟這個(gè)有關(guān)。
針對(duì)不同規(guī)模的App算谈,我們的目標(biāo)應(yīng)該有所取舍涩禀。例如,對(duì)于像手機(jī)QQ這種集整個(gè)SNG的代碼大成擼出來的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)過程分為四個(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決定了用戶視覺上的等待出現(xiàn)有用信息所需要的時(shí)長(zhǎng)喇完,1+2+3+4決定了我們需要多少時(shí)間才能讓我們需要展示給用戶的所有信息全部出現(xiàn)。
淘寶的iOS客戶端無疑是各部分都做得非常優(yōu)秀的典型剥啤。它所承載的業(yè)務(wù)完全不比微信和手機(jī)QQ少锦溪,但幾乎瞬間完成了啟動(dòng)不脯,并利用緩存機(jī)制使得用戶馬上看到“貌似完整”的界面,然后立即又刷新了剛剛聯(lián)網(wǎng)更新回來的信息刻诊。也就是說防楷,無論是技術(shù)上還是視覺上,它都非常的“快”则涯。
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è)加回來。如果有靠譜的方法浮入,歡迎大家補(bǔ)充一下龙优。
2. 移除不需要用到的類 項(xiàng)目做久了總有一些吊詭的類像幽靈一樣驅(qū)之不去羊异,由于【不要相信產(chǎn)品經(jīng)理】的思想作怪事秀,需求變更后,有些類可能用不上了野舶,但卻因?yàn)閾?dān)心需求再變回來就沒有移除掉易迹,后來就徹底忘記要移除了。
為了解決這個(gè)歷史問題平道,在這個(gè)過程中我試過多種方法來掃描沒有用到的類睹欲,其中有一種是編譯后對(duì)ObjC類的指針引用進(jìn)行反向掃描,可惜實(shí)際上收獲不是很明顯一屋,而且還要寫很多例外代碼來處理一些特殊情況窘疮。后來發(fā)現(xiàn)一個(gè)叫做fui(Find Unused Imports)的開源項(xiàng)目能很好的分析出不再使用的類,準(zhǔn)確率非常高冀墨,唯一的問題是它處理不了動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)里提供的類闸衫,也處理不了C++的類模板。
使用方法是在Terminal中cd到項(xiàng)目所在的目錄诽嘉,然后執(zhí)行fui find蔚出,然后等上那么幾分鐘(是的你沒有看錯(cuò),真的需要好幾分鐘甚至需要更長(zhǎng)的時(shí)間)虫腋,就可以得到一個(gè)列表了骄酗。由于這個(gè)工具還不是100%靠譜,可根據(jù)這個(gè)列表悦冀,在Xcode中手動(dòng)檢查并刪除不再用到的類趋翻。
實(shí)際上,日常對(duì)代碼工程的維護(hù)非常重要盒蟆,如果制定好一套半廢棄代碼的維護(hù)方法嘿歌,小問題就不會(huì)積累成大問題掸掏。有時(shí)候?qū)τ谝恍簳r(shí)不再使用的代碼,我也很糾結(jié)于要不要svn rm宙帝,因?yàn)閺拇a歷史中找刪除掉的文件還是不太方便丧凤。不知道大家有沒有相關(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)化作用。不過個(gè)人認(rèn)為也不能因?yàn)樗加脝?dòng)時(shí)間而去逃避使用擴(kuò)展鸳君,畢竟程序員的時(shí)間比CPU的時(shí)間值錢农渊,這里只是強(qiáng)調(diào)要合并一些在工程、架構(gòu)上沒有太大意義的擴(kuò)展或颊。
4. 壓縮資源圖片 壓縮圖片為什么能加快啟動(dòng)速度呢砸紊?因?yàn)閱?dòng)的時(shí)候大大小小的圖片加載個(gè)十來二十個(gè)是很正常的,圖片小了囱挑,IO操作量就小了醉顽,啟動(dòng)當(dāng)然就會(huì)快了。
事實(shí)上平挑,Xcode在編譯App的時(shí)候游添,已經(jīng)自動(dòng)把需要打包到App里的資源圖片壓縮過一遍了。然而Xcode的壓縮會(huì)相對(duì)比較保守通熄。另一方面唆涝,我們正常的設(shè)計(jì)師由于需要符合其正常的審美需要生成的正常的PNG圖片,因此圖片大小是比較大的唇辨,然而如果以程序員的直男審美而采用過激的壓縮會(huì)直接激怒設(shè)計(jì)師廊酣。
解決各種矛盾的方法就是要找出一種相當(dāng)靠譜的壓縮方法,而且最好是基本無損的助泽,而且壓縮率還要特別高啰扛,至少要比Xcode自動(dòng)壓縮的效果要更好才有意義。經(jīng)過各種試驗(yàn)嗡贺,最后發(fā)現(xiàn)唯一可靠的壓縮算法是TinyPNG隐解,其它各種方法,要么沒效果诫睬,要么產(chǎn)生色差或模糊煞茫。但是非常可惜的是TinyPNG并不是完全免費(fèi)的,而且需要通過網(wǎng)絡(luò)請(qǐng)求來壓縮圖片(應(yīng)該是為了保護(hù)其牛逼的壓縮算法)续徽。
為了解決這個(gè)問題蚓曼,我寫了一個(gè)類來執(zhí)行這個(gè)請(qǐng)求,請(qǐng)參見閱讀原文里的SSTinyPNGRequest和SSPNGCompressor钦扭。因?yàn)檫@個(gè)項(xiàng)目只有我一個(gè)人在用所以代碼寫得有點(diǎn)隨意纫版,有問題可以私聊也可以在評(píng)論里問,有改進(jìn)的方法也非常歡迎指正客情。另外說明一下其弊,使用這個(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í)間沒有控制好,里面的邏輯亂得有點(diǎn)丟人摔蓝。因?yàn)榭赡苌婕暗揭恍╉?xiàng)目的安全性問題赂苗,這里不能分享所有的優(yōu)化細(xì)節(jié)及發(fā)現(xiàn)的思路。僅列出在applicationWillFinishLaunching中主要需要處理的業(yè)務(wù)及相關(guān)問題的改進(jìn)方案项鬼。
這里大部分都是一些苦逼活哑梳,但有一點(diǎn)特別值得分享的是劲阎,有一些優(yōu)化绘盟,是無法在數(shù)據(jù)上體現(xiàn)的,但是視覺上卻能給用戶較大的提升悯仙。例如在【各種業(yè)務(wù)請(qǐng)求配置更新】的部分龄毡,經(jīng)過分析優(yōu)化后,啟動(dòng)過程并發(fā)的http請(qǐng)求數(shù)量從66條壓縮到了23條锡垄,如此一來為啟動(dòng)成功后新聞資訊及其圖片的加載留出了更多的帶寬,從而保證了在第一時(shí)間完成新聞資訊的加載。實(shí)際測(cè)試表明德频,光做KPI的事情是不夠的徘禁,人還是需要有點(diǎn)理想,經(jīng)過優(yōu)化千贯,在視覺體驗(yàn)上進(jìn)步非常明顯屯仗。
另外,過程中請(qǐng)教過SNG的大牛們搔谴,聽說他們因?yàn)樾枰赼pplicationWillFinishLaunching里處理的業(yè)務(wù)更多魁袜,所以還做了管理器管理這些任務(wù),不過因?yàn)閃iFi管家是個(gè)小項(xiàng)目,有點(diǎn)殺雞用牛刀的感覺峰弹,因此沒有深入研究店量。
6. 優(yōu)化rootViewController加載 考慮到我作為一只程序猴,工資還行鞠呈,為了給公司節(jié)約成本融师,在優(yōu)化之前,當(dāng)然需要先測(cè)試一下哪些ViewController的加載耗時(shí)比較大蚁吝,然后再深入到具體業(yè)務(wù)中看哪些部分存在較大的優(yōu)化空間诬滩。同時(shí),先做優(yōu)化效果明顯的部分也有利于增強(qiáng)自己的信心灭将。
在開始講述問題之前疼鸟,我們先來看一下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è)問題比較嚴(yán)重:
** 1. 有些程序員可能架構(gòu)意識(shí)不是太強(qiáng),直接在tabBarController的啟動(dòng)過程中插入了各種奇怪的業(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)畫,由于需要疊加較多圖片矿筝,這部分視圖的加載耗時(shí)較多起便。**
由于隨著幾次改版之后,連接頁(yè)的UI架構(gòu)已經(jīng)變得很不合理窖维,歷史包袱還是比較重的榆综,而且耦合比較嚴(yán)重,幾乎無法改動(dòng)铸史,因此決定重構(gòu)鼻疮。至于tabBarController,檢查代碼后決定簡(jiǎn)單的把不相關(guān)的業(yè)務(wù)做一些遷移琳轿,優(yōu)化childViewController的加載過程判沟,不作重構(gòu)。
由于本篇主要講啟動(dòng)性能優(yōu)化崭篡,重構(gòu)涉及的軟件工程和設(shè)計(jì)模式方面的東西就不詳細(xì)論述了挪哄,對(duì)啟動(dòng)優(yōu)化的過程,主要是使用了更合理的分層結(jié)構(gòu)琉闪,使得啟動(dòng)得以在更短的時(shí)間內(nèi)完成迹炼。
至此,WiFi管家的啟動(dòng)性能基本優(yōu)化完畢颠毙。
7. 挖掘最后一點(diǎn)性能優(yōu)化 由于WiFi管家是一個(gè)具有WiFi連接能力的App斯入,因此有可能在后臺(tái)過程中完成冷啟動(dòng)過程(實(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)放過的一些小細(xì)節(jié)
? 異步操作并不影響指標(biāo)窗骑,但有可能影響交互體驗(yàn)女责,例如大量網(wǎng)絡(luò)請(qǐng)求導(dǎo)致數(shù)據(jù)擁堵
? 有時(shí)候一些交互上的優(yōu)化比技術(shù)手段效果更明顯,視覺上的快決不是冰冷的數(shù)據(jù)可以解釋的创译,好好和你們的設(shè)計(jì)師談?wù)剟?dòng)畫
7.0x8badf00d表示是什么抵知?
0x8badf00d: 讀做 “ate bad food”! (把數(shù)字換成字母,是不是很像 :p)該編碼表示應(yīng)用是因?yàn)榘l(fā)生watchdog超時(shí)而被iOS終止的软族。 通常是應(yīng)用花費(fèi)太多時(shí)間而無法啟動(dòng)刷喜、終止或響應(yīng)用系統(tǒng)事件。
0xbad22222: 該編碼表示 VoIP 應(yīng)用因?yàn)檫^于頻繁重啟而被終止立砸。
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ù)蘋果文檔, 強(qiáng)制退出發(fā)生在用戶長(zhǎng)按開關(guān)按鈕直到出現(xiàn) “滑動(dòng)來關(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ù)提供加密方案猛铅,有效防止通過網(wǎng)絡(luò)接口的攔截獲取數(shù)據(jù)
4.方法體,方法名高級(jí)混淆
iOS應(yīng)用防反編譯加密技術(shù)之四:對(duì)應(yīng)用程序的方法名和方法體進(jìn)行混淆凤藏,保證源碼被逆向后無法解析代碼
5.程序結(jié)構(gòu)混排加密
iOS應(yīng)用防反編譯加密技術(shù)之五:對(duì)應(yīng)用程序邏輯結(jié)構(gòu)進(jìn)行打亂混排奸忽,保證源碼可讀性降到最低
6.借助第三方APP加固,例如:網(wǎng)易云易盾
9.說說你遇到到的技術(shù)難點(diǎn)揖庄?
你遇到的問題難度栗菜,一定程度決定了你的水平。如實(shí)反映就好蹄梢。只是一道承上啟下的問題疙筹!
以下問題是群友提供的:
問題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è)試來看目前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部分展開: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ì)精度的要求沒有那么高贬蛙。
下面著重介紹我們最常用的基于球面模型的地理空間距離計(jì)算公式雨女,推導(dǎo)也只需要高中數(shù)學(xué)知識(shí)即可。
<center style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(25, 25, 25); text-decoration-style: initial; text-decoration-color: initial; font-size: 12px; letter-spacing: normal;">
<center style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></center>
</center>
該模型將地球看成圓球阳准,假設(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來篩選團(tuán)購(gòu)單子和商家操漠,lucene使用了spatial4j工具包來計(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ì)算精度的過多顧慮的問題卧蜓。
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)特別快弥奸,未來北京地區(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)行)
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
POI數(shù)目
|
耗時(shí)(ms)
|
|
5w
|
7
|
|
10w
|
14
|
|
100w
|
144
|
4 優(yōu)化方案
通過抓棧我們發(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ì)算用戶位置與所有篩選出來的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是地球半徑)剪验。
此方法性能如下:
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
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)化后性能如下饰序。
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
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ì)算,也就是說兩個(gè)點(diǎn)之間的距離一般不會(huì)超過200多千米规哪。由于范圍小求豫,可以認(rèn)為經(jīng)線和緯線是垂直的,如圖所示诉稍,要求A(116.8蝠嘉,39,78)和B(116.9,39.68)兩點(diǎn)的距離均唉,我們可以先求出南北方向距離AM是晨,然后求出東西方向距離BM,最后求矩形對(duì)角線距離舔箭,即sqrt(AMAM + BMBM)罩缴。
<center style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(25, 25, 25); text-decoration-style: initial; text-decoration-color: initial; font-size: 12px; letter-spacing: normal;">
<center style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></center>
</center>
南北方向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è)方法的相差情況桶至。
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
測(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
|
可以看到兩者在百米伪窖、千米尺度上幾乎沒有差別逸寓,在萬米尺度上也僅有分米的差別,此外由于我們的業(yè)務(wù)是在一個(gè)城市范圍內(nèi)進(jìn)行篩選排序覆山,所以我們選擇了北京左下角和右上角兩點(diǎn)進(jìn)行比較竹伸,兩點(diǎn)相距有260多千米,兩個(gè)方法差別17m汹买。從精度上看該優(yōu)化方法能滿足我們應(yīng)用需求佩伤。
1.2)性能驗(yàn)證
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
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)式來擬合cos三角函數(shù)见妒,這樣不就可以將三角函數(shù)轉(zhuǎn)換為加減乘除了嘛!
首先決定多項(xiàng)式的最高次數(shù)甸陌,次數(shù)為1和2顯然都無法很好擬合cos函數(shù)须揣,那么我們選擇3先嘗試吧,注:最高次數(shù)不是越多越好钱豁,次數(shù)越高會(huì)產(chǎn)生過擬合問題耻卡。
使用org.apache.commons.math3這一數(shù)學(xué)工具包來進(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<WeightedObservedPoint> weightedObservedPoints = new ArrayList<WeightedObservedPoint>();
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è)方法的相差情況蜒简。
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
測(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)證
<colgroup style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"><col style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></colgroup>
|
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空間索引放刨,而通過空間轉(zhuǎn)換變成三維數(shù)據(jù)后不能直接使用。我們的應(yīng)用有附近范圍篩選功能(例如附近5km的團(tuán)購(gòu)單子)尸饺,通過geohash空間索引可以提高范圍篩選的效率进统;
b)坐標(biāo)轉(zhuǎn)換方法增大內(nèi)存開銷。我們會(huì)將坐標(biāo)寫入倒排索引中浪听,之前坐標(biāo)是2列(經(jīng)度和緯度)螟碎,現(xiàn)在變成3列(x,y,z),在使用中我們往往會(huì)將這數(shù)據(jù)放入到cache中迹栓,因此會(huì)增大內(nèi)存開銷掉分;
c)坐標(biāo)轉(zhuǎn)換方法增大建索引開銷。此方法本質(zhì)上是將計(jì)算從查詢階段放至到索引階段克伊,因此提高了建索引的開銷酥郭。
基于上述原因我們?cè)趯?shí)際應(yīng)用中采用簡(jiǎn)化距離公式方法(通過三次多項(xiàng)式來擬合cos三角函數(shù)),此方法在團(tuán)購(gòu)篩選和商家篩選的距離排序愿吹、智能排序中已經(jīng)開始使用不从,與之前相比,篩選團(tuán)購(gòu)時(shí)北京全城美食品類距離排序響應(yīng)時(shí)間從40ms下降為20ms犁跪。
問題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)倡勇?
代碼整齊,分類明確嘉涌,沒有common妻熊,沒有core
不用文檔,或很少文檔仑最,就能讓業(yè)務(wù)方上手
思路和方法要統(tǒng)一扔役,盡量不要多元
沒有橫向依賴,萬不得已不出現(xiàn)跨層訪問
對(duì)業(yè)務(wù)方該限制的地方有限制警医,該靈活的地方要給業(yè)務(wù)方創(chuàng)造靈活實(shí)現(xiàn)的條件
易測(cè)試亿胸,易拓展
保持一定量的超前性
接口少坯钦,接口參數(shù)少
高性能
第一類:精簡(jiǎn)型應(yīng)用架構(gòu)
這類架構(gòu)的文章分析主要還是圍繞MVC展開,以蘋果自帶UIViewController優(yōu)劣為出發(fā)點(diǎn)侈玄,再結(jié)合主流的MVP婉刀,MVVM,MVCS等變種進(jìn)行分析演變序仙。這類的探討重點(diǎn)在于M突颊,V,C三類角色的定義以及之間的數(shù)據(jù)事件流向的規(guī)范潘悼。很多小型應(yīng)用所面臨的問題及其架構(gòu)層面的解決方案都集中在這一類律秃。
第二類:綜合型應(yīng)用架構(gòu)
對(duì)于用戶量級(jí)在千萬級(jí)或以上的應(yīng)用來說,MVC這一層面的思考已無法應(yīng)對(duì)業(yè)務(wù)瘋狂增長(zhǎng)所帶來的負(fù)擔(dān)治唤。這類應(yīng)用往往需要專業(yè)資深的架構(gòu)師出面進(jìn)行深層次的思考設(shè)計(jì)棒动,業(yè)內(nèi)不少大廠如淘寶,天貓肝劲,攜程等都做過一些分享迁客。不過到了這一層級(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)化可以分為兩塊,第一是組件或模塊劃分的粒度越來越細(xì)险掀,第二是組件模塊的深度優(yōu)化沪袭,比如網(wǎng)絡(luò)層的深度優(yōu)化,sqlite優(yōu)化(多線程樟氢,F(xiàn)TS冈绊,安全等),數(shù)據(jù)加密埠啃,HotFix死宣,Hybrid等,一些開源的第三方庫(kù)已不能滿足要求碴开,需要團(tuán)隊(duì)自己重造輪子毅该。這一層面的架構(gòu)設(shè)計(jì)涉及面廣博秫,對(duì)架構(gòu)師,團(tuán)隊(duì)技術(shù)人員的技術(shù)深度和業(yè)務(wù)理解能力有較高要求眶掌,短短一篇技術(shù)文章往往只能走馬觀花的介紹個(gè)大概台盯,每一次優(yōu)化幾乎都可以作為一個(gè)專題來講解。
第四類:組織型應(yīng)用架構(gòu)
這類架構(gòu)在第三類的基礎(chǔ)之上更進(jìn)了一步畏线,除了關(guān)注系統(tǒng)層面的架構(gòu)設(shè)計(jì)之外静盅,更對(duì)團(tuán)隊(duì)或部門之間協(xié)作方式,各系統(tǒng)模塊的演進(jìn)方式寝殴,產(chǎn)品發(fā)布流程等都做了規(guī)范蒿叠。除去業(yè)務(wù)膨脹帶來的壓力,人員增長(zhǎng)蚣常,各團(tuán)隊(duì)協(xié)作依賴增強(qiáng)等都會(huì)對(duì)app的質(zhì)量市咽,迭代速度產(chǎn)生影響,這些問題也需要從架構(gòu)層面去解決抵蚊。這類結(jié)合技術(shù)架構(gòu)和組織架構(gòu)的分享還比較少施绎。
以上四種類型的架構(gòu)又可以看做一般App從簡(jiǎn)至繁,公司規(guī)模隨之增長(zhǎng)的演進(jìn)過程贞绳,技術(shù)圈絕大部分的架構(gòu)類分享文章都可以歸為上述四類谷醉。
對(duì)于什么是架構(gòu)的學(xué)術(shù)定義,似乎大家并不太在意冈闭,更關(guān)心的是如何解決自身項(xiàng)目當(dāng)下的問題俱尼。雖然在我看來第一類架構(gòu)更像是在討論設(shè)計(jì)模式,但這里面確實(shí)又有非常多的知識(shí)可以深入挖掘萎攒,這里就把所有“解決應(yīng)用整體設(shè)計(jì)問題”的討論都?xì)w類于架構(gòu)這一話題遇八。
值得一提的是,架構(gòu)師的視野和積累一般都受限于自己所經(jīng)歷項(xiàng)目及業(yè)務(wù)的規(guī)模耍休。如果有機(jī)會(huì)刃永,工程師還是應(yīng)該盡可能去BAT這類巨頭級(jí)公司歷練一下,知識(shí)深度和廣度的構(gòu)建絕非紙上可得羊精。
可從以下方面回答:(舉例說明斯够,攜程開發(fā)提供)
核心功能SDK化
通訊、定位园匹、Hybrid雳刺、數(shù)據(jù)庫(kù)、登錄裸违、分享掖桦、基礎(chǔ)庫(kù)等
直接提供給其他BU獨(dú)立App使用
公用業(yè)務(wù)功能組件化
地圖、日歷供汛、城市枪汪、圖片涌穆、通訊錄等13個(gè)公共組件
減少各BU重復(fù)開發(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ù)連接/讀/寫不同階段使用重試機(jī)制
使用IP列表避免DNS解析失敗或者劫持
根據(jù)網(wǎng)絡(luò)延遲選擇服務(wù)端IP(使用Ping)
使用ProtocolBuffer+Gzip減少Payload
10.說說你了解的第三方原理或底層知識(shí)罩锐?
Runtime、Runloop卤唉、block
SD原理涩惑、YYCache、GCD源碼分析桑驱、JSPatch原理等竭恬!
以上面試題莲镣,部分答案來自群主筆記遏乔!僅供參考!