題庫整理(二)

1.mach-0文件的結(jié)構(gòu)都有哪些陕见,iOS程序main()函數(shù)之前和之后分別都做了些什么浪读?

  • Mach-O文件格式是OS X與iOS系統(tǒng)上的可執(zhí)行文件格式,像我們編譯過程產(chǎn)生的.O文件,以及程序的可執(zhí)行文件,動態(tài)庫等都是Mach-O文件辅搬,它的結(jié)構(gòu)如下:


    mach-0.png

Header: 保存了一些基本信息,包括了該文件運行的平臺脖旱、文件類型堪遂、LoadCommands的個數(shù)等。
LoadCommands: 可以理解為加載命令萌庆,在加載Mach-O文件時會使用這里的數(shù)據(jù)來確定內(nèi)存的分布以及相關(guān)的加載命令溶褪。比如我們的main函數(shù)的加載地址,程序所需的dyld的文件路徑踊兜,以及相關(guān)依賴庫的文件路徑竿滨。
Data:這里包含了具體的代碼佳恬、數(shù)據(jù)等捏境。

  • t(App總啟動時間) = t1(main()之前的加載時間) + t2(main()之后的加載時間),t1 = 系統(tǒng)dylib(動態(tài)鏈接庫)和自身App可執(zhí)行文件的加載;
    t2 = main方法執(zhí)行之后到AppDelegate類中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法執(zhí)行結(jié)束前這段時間毁葱,主要是構(gòu)建第一個界面垫言,并完成渲染展示。

App開始啟動后倾剿,系統(tǒng)首先加載可執(zhí)行文件(自身App的所有.o文件的集合)筷频,然后加載動態(tài)鏈接庫dyld,dyld是一個專門用來加載動態(tài)鏈接庫的庫前痘。 執(zhí)行從dyld開始凛捏,dyld從可執(zhí)行文件的依賴開始, 遞歸加載所有的依賴動態(tài)鏈接庫。
動態(tài)鏈接庫包括:iOS 中用到的所有系統(tǒng) framework芹缔,加載OC runtime方法的libobjc坯癣,系統(tǒng)級別的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)最欠。

無論對于系統(tǒng)的動態(tài)鏈接庫還是對于App本身的可執(zhí)行文件而言示罗,他們都算是image(鏡像),而每個App都是以image(鏡像)為單位進(jìn)行加載的芝硬,那么image究竟包括哪些呢蚜点?

1.executable可執(zhí)行文件 比如.o文件。
2.dylib 動態(tài)鏈接庫 framework就是動態(tài)鏈接庫和相應(yīng)資源包含在一起的一個文件夾結(jié)構(gòu)拌阴。
3.bundle 資源文件 只能用dlopen加載绍绘,不推薦使用這種方式加載。

所有動態(tài)鏈接庫和我們App中的靜態(tài)庫.a和所有類文件編譯后的.o文件最終都是由dyld(the dynamic link editor)Apple的動態(tài)鏈接器來加載到內(nèi)存中。每個image都是由一個叫做ImageLoader的類來負(fù)責(zé)加載(一一對應(yīng)).

  • 動態(tài)鏈接庫加載的具體流程

動態(tài)鏈接庫的加載步驟具體分為5步:
1.load dylibs image 讀取庫鏡像文件
2.Rebase image
3.Bind image

4.Objc setup
5.initializers

load dylibs image
在每個動態(tài)庫的加載過程中陪拘, dyld需要:
分析所依賴的動態(tài)庫
找到動態(tài)庫的mach-o文件
打開文件
驗證文件
在系統(tǒng)核心注冊文件簽名
對動態(tài)庫的每一個segment調(diào)用mmap()

tips:
通常的实辑,一個App需要加載100到400個dylibs, 但是其中的系統(tǒng)庫被優(yōu)化藻丢,可以很快的加載剪撬。 針對這一步驟的優(yōu)化有:
1.減少非系統(tǒng)庫的依賴
2.合并非系統(tǒng)庫
3.使用靜態(tài)資源,比如把代碼加入主程序

rebase/bind
由于ASLR(address space layout randomization)的存在悠反,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內(nèi)存中的加載地址每次啟動都不固定残黑,所以需要這2步來修復(fù)鏡像中的資源指針,來指向正確的地址斋否。 rebase修復(fù)的是指向當(dāng)前鏡像內(nèi)部的資源指針梨水; 而bind指向的是鏡像外部的資源指針。
rebase步驟先進(jìn)行茵臭,需要把鏡像讀入內(nèi)存疫诽,并以page為單位進(jìn)行加密驗證,保證不會被篡改旦委,所以這一步的瓶頸在IO奇徒。bind在其后進(jìn)行,由于要查詢符號表缨硝,來指向跨鏡像的資源摩钙,加上在rebase階段,鏡像已被讀入和加密驗證查辩,所以這一步的瓶頸在于CPU計算胖笛。

tips:
優(yōu)化該階段的關(guān)鍵在于減少__DATA segment中的指針數(shù)量。我們可以優(yōu)化的點有:
1.減少Objc類數(shù)量宜岛, 減少selector數(shù)量
2.減少C++虛函數(shù)數(shù)量
3.轉(zhuǎn)而使用swift stuct(其實本質(zhì)上就是為了減少符號的數(shù)量)

Objc setup
這一步主要工作是:
1.注冊O(shè)bjc類 (class registration)
2.把category的定義插入方法列表 (category registration)
3.保證每一個selector唯一 (selctor uniquing)

initializers
以上三步屬于靜態(tài)調(diào)整(fix-up)长踊,都是在修改__DATA segment中的內(nèi)容,而這里則開始動態(tài)調(diào)整萍倡,開始在堆和堆棧中寫入內(nèi)容身弊。 在這里的工作有:
1.Objc的+load()函數(shù)
2.C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
3.非基本類型的C++靜態(tài)全局變量的創(chuàng)建(通常是類或結(jié)構(gòu)體)(non-trivial initializer) 比如一個全局靜態(tài)結(jié)構(gòu)體的構(gòu)建,如果在構(gòu)造函數(shù)中有繁重的工作遣铝,那么會拖慢啟動速度

main()調(diào)用之前總結(jié):

對于main()調(diào)用之前的耗時我們可以優(yōu)化的點有:
1.減少不必要的framework佑刷,因為動態(tài)鏈接比較耗時
2.check framework應(yīng)當(dāng)設(shè)為optional和required,如果該framework在當(dāng)前App支持的所有iOS系統(tǒng)版本都存在酿炸,那么就設(shè)為required瘫絮,否則就設(shè)為optional,因為optional會有些額外的檢查
3.合并或者刪減一些OC類填硕,關(guān)于清理項目中沒用到的類麦萤,使用工具AppCode代碼檢查功能鹿鳖。
4.刪減一些無用的靜態(tài)變量
5.刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法
6.將不必須在+load方法中做的事情延遲到+initialize中
7.盡量不要用C++虛函數(shù)(創(chuàng)建虛函數(shù)表有開銷)

  • main()調(diào)用之后的加載時間

在main()被調(diào)用之后,App的主要工作就是初始化必要的服務(wù)壮莹,顯示首頁內(nèi)容等翅帜。而我們的優(yōu)化也是圍繞如何能夠快速展現(xiàn)首頁來開展。 App通常在AppDelegate類中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中創(chuàng)建首頁需要展示的view命满,然后在當(dāng)前runloop的末尾涝滴,主動調(diào)用CA::Transaction::commit完成視圖的渲染。
而視圖的渲染主要涉及三個階段:
1.準(zhǔn)備階段 這里主要是圖片的解碼
2.布局階段 首頁所有UIView的- (void)layoutSubViews()運行
3.繪制階段 首頁所有UIView的- (void)drawRect:(CGRect)rect運行
再加上啟動之后必要服務(wù)的啟動胶台、必要數(shù)據(jù)的創(chuàng)建和讀取歼疮,這些就是我們可以嘗試優(yōu)化的地方

因此,對于main()函數(shù)調(diào)用之前我們可以優(yōu)化的點有:

1.不使用xib诈唬,直接視用代碼加載首頁視圖
2.NSUserDefaults實際上是在Library文件夾下會生產(chǎn)一個plist文件韩脏,如果文件太大的話一次能讀取到內(nèi)存中可能很耗時,這個影響需要評估铸磅,如果耗時很大的話需要拆分(需考慮老版本覆蓋安裝兼容問題)
3.每次用NSLog方式打印會隱式的創(chuàng)建一個Calendar赡矢,因此需要刪減啟動時各業(yè)務(wù)方打的log,或者僅僅針對內(nèi)測版輸出log
4.梳理應(yīng)用啟動時發(fā)送的所有網(wǎng)絡(luò)請求阅仔,是否可以統(tǒng)一在異步線程請求

  • 針對app我們可以優(yōu)化的點如下:

1.純代碼方式而不是storyboard加載首頁UI吹散。
2.對didFinishLaunching里的函數(shù)考慮能否挖掘可以延遲加載或者懶加載,需要與各個業(yè)務(wù)方pm和rd共同check 對于一些已經(jīng)下線的業(yè)務(wù)霎槐,刪減冗余代碼送浊。
3.對于一些與UI展示無關(guān)的業(yè)務(wù),如微博認(rèn)證過期檢查丘跌、圖片最大緩存空間設(shè)置等做延遲加載
4.對實現(xiàn)了+load()方法的類進(jìn)行分析,盡量將load里的代碼延后調(diào)用唁桩。
5.上面統(tǒng)計數(shù)據(jù)顯示展示feed的導(dǎo)航控制器頁面(NewsListViewController)比較耗時闭树,對于viewDidLoad以及viewWillAppear方法中盡量去嘗試少做,晚做荒澡,不做报辱。

2.app哪些情況會產(chǎn)生崩潰
1.數(shù)組越界訪問
2.調(diào)用了未實現(xiàn)的方法
3.野指針
4.返回空cell
5.類釋放時未remove通知,之后收到通知
6.類釋放時delegate未置空单山,之后被回調(diào)
7.內(nèi)存暴漲
8.app升級改變了數(shù)據(jù)結(jié)構(gòu)
9.字符串的截取越界導(dǎo)致的崩潰
3.事件的傳遞和響應(yīng)機(jī)制

  • 事件的產(chǎn)生

1.發(fā)生觸摸事件后碍现,系統(tǒng)會將該事件加入到一個由UIApplication管理的事件隊列中,為什么是隊列而不是棧?因為隊列的特點是FIFO米奸,即先進(jìn)先出昼接,先產(chǎn)生的事件先處理才符合常理,所以把事件添加到隊列悴晰。
2.UIApplication會從事件隊列中取出最前面的事件慢睡,并將事件分發(fā)下去以便處理逐工,通常,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)漂辐。
主窗口會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件泪喊,這也是整個事件處理過程的第一步。
3.找到合適的視圖控件后髓涯,就會調(diào)用視圖控件的touches方法來作具體的事件處理袒啼。

  • 事件的傳遞

1.觸摸事件的傳遞是從父控件傳遞到子控件
2.也就是UIApplication->window->尋找處理事件最合適的view
tips: 如果父控件不能接受觸摸事件,那么子控件就不可能接收到觸摸事件

事件的傳遞順序是這樣的:
  產(chǎn)生觸摸事件->UIApplication事件隊列->[UIWindow hitTest:withEvent:]->返回更合適的view->[子控件 hitTest:withEvent:]->返回最合適的view

事件傳遞給窗口或控件的后纬纪,就調(diào)用hitTest:withEvent:方法尋找更合適的view瘤泪。所以是,先傳遞事件育八,再根據(jù)事件在自己身上找更合適的view对途。
不管子控件是不是最合適的view,系統(tǒng)默認(rèn)都要先把事件傳遞給子控件髓棋,經(jīng)過子控件調(diào)用子控件自己的hitTest:withEvent:方法驗證后才知道有沒有更合適的view实檀。即便父控件是最合適的view了,子控件的hitTest:withEvent:方法還是會調(diào)用按声,不然怎么知道有沒有更合適的膳犹!即,如果確定最終父控件是最合適的view签则,那么該父控件的子控件的hitTest:withEvent:方法也是會被調(diào)用的须床。

  • 事件的響應(yīng)

觸摸事件處理的整體過程
1>用戶點擊屏幕后產(chǎn)生的一個觸摸事件,經(jīng)過一系列的傳遞過程后渐裂,會找到最合適的視圖控件來處理這個事件2>找到最合適的視圖控件后豺旬,就會調(diào)用控件的touches方法來作具體的事件處理touchesBegan…touchesMoved…touchedEnded…3>這些touches方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞(也就是touch方法默認(rèn)不處理事件,只傳遞事件)柒凉,將事件交給上一個響應(yīng)者進(jìn)行處理

響應(yīng)者鏈條:在iOS程序中無論是最后面的UIWindow還是最前面的某個按鈕族阅,它們的擺放是有前后關(guān)系的,一個控件可以放到另一個控件上面或下面膝捞,那么用戶點擊某個控件時是觸發(fā)上面的控件還是下面的控件呢坦刀,這種先后關(guān)系構(gòu)成一個鏈條就叫“響應(yīng)者鏈”。也可以說蔬咬,響應(yīng)者鏈?zhǔn)怯啥鄠€響應(yīng)者對象連接起來的鏈條鲤遥。

事件的傳遞與響應(yīng):

1、當(dāng)一個事件發(fā)生后林艘,事件會從父控件傳給子控件盖奈,也就是說由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的傳遞,也就是尋找最合適的view的過程北启。
2卜朗、接下來是事件的響應(yīng)拔第。首先看initial view能否處理這個事件,如果不能則會將事件傳遞給其上級視圖(inital view的superView)场钉;如果上級視圖仍然無法處理則會繼續(xù)往上傳遞蚊俺;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件逛万;如果不能則接著判斷該視圖控制器能否處理此事件泳猬,如果還是不能則繼續(xù)向上傳 遞;(對于第二個圖視圖控制器本身還在另一個視圖控制器中宇植,則繼續(xù)交給父視圖控制器的根視圖得封,如果根視圖不能處理則交給父視圖控制器處理);一直到 window指郁,如果window還是不能處理此事件則繼續(xù)交給application處理忙上,如果最后application還是不能處理此事件則將其丟棄。
3闲坎、在事件的響應(yīng)中疫粥,如果某個控件實現(xiàn)了touches...方法,則這個事件將由該控件來接受腰懂,如果調(diào)用了[supertouches….];就會將事件順著響應(yīng)者鏈條往上傳遞梗逮,傳遞給上一個響應(yīng)者;接著就會調(diào)用上一個響應(yīng)者的touches….方法绣溜。

  • 如何做到一個事件多個對象處理:

因為系統(tǒng)默認(rèn)做法是把事件上拋給父控件慷彤,所以可以通過重寫自己的touches方法和父控件的touches方法來達(dá)到一個事件多個對象處理的目的。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
// 1.自己先處理事件...
NSLog(@"do somthing...");
// 2.再調(diào)用系統(tǒng)的默認(rèn)做法怖喻,再把事件交給上一個響應(yīng)者處理
[super touchesBegan:touches withEvent:event]; 
}
  • tips:事件的傳遞和響應(yīng)的區(qū)別:

事件的傳遞是從上到下(父控件到子控件)底哗,事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件。

4.dispatch_barrier_async和dispatch_group

  • dispatch_barrier_async使用Barrier Task方法Dispatch Barrier解決多線程并發(fā)讀寫同一個資源發(fā)生死鎖

Dispatch Barrier確保提交的閉包是指定隊列中在特定時段唯一在執(zhí)行的一個罢防。在所有先于Dispatch Barrier的任務(wù)都完成的情況下這個閉包才開始執(zhí)行艘虎。輪到這個閉包時barrier會執(zhí)行這個閉包并且確保隊列在此過程不會執(zhí)行其它任務(wù)。閉包完成后隊列恢復(fù)咒吐。需要注意dispatch_barrier_async只在自己創(chuàng)建的隊列上有這種作用,在全局并發(fā)隊列和串行隊列上属划,效果和dispatch_sync一樣恬叹。

  • Block組合Dispatch_groups

dispatch groups是專門用來監(jiān)視多個異步任務(wù)。dispatch_group_t實例用來追蹤不同隊列中的不同任務(wù)同眯。
當(dāng)group里所有事件都完成GCD API有兩種方式發(fā)送通知绽昼,第一種是dispatch_group_wait,會阻塞當(dāng)前進(jìn)程须蜗,等所有任務(wù)都完成或等待超時硅确。第二種方法是使用dispatch_group_notify目溉,異步執(zhí)行閉包,不會阻塞菱农。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缭付,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子循未,更是在濱河造成了極大的恐慌陷猫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件的妖,死亡現(xiàn)場離奇詭異绣檬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嫂粟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門娇未,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人星虹,你說我怎么就攤上這事零抬。” “怎么了搁凸?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵媚值,是天一觀的道長。 經(jīng)常有香客問我护糖,道長褥芒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任嫡良,我火速辦了婚禮锰扶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寝受。我一直安慰自己坷牛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布很澄。 她就那樣靜靜地躺著京闰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甩苛。 梳的紋絲不亂的頭發(fā)上蹂楣,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音讯蒲,去河邊找鬼痊土。 笑死,一個胖子當(dāng)著我的面吹牛墨林,可吹牛的內(nèi)容都是我干的赁酝。 我是一名探鬼主播犯祠,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酌呆!你這毒婦竟也來了衡载?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤肪笋,失蹤者是張志新(化名)和其女友劉穎月劈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藤乙,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡猜揪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坛梁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片而姐。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖划咐,靈堂內(nèi)的尸體忽然破棺而出拴念,到底是詐尸還是另有隱情,我是刑警寧澤褐缠,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布政鼠,位于F島的核電站,受9級特大地震影響队魏,放射性物質(zhì)發(fā)生泄漏公般。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一胡桨、第九天 我趴在偏房一處隱蔽的房頂上張望官帘。 院中可真熱鬧,春花似錦昧谊、人聲如沸刽虹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涌哲。三九已至,卻和暖如春尚镰,著一層夾襖步出監(jiān)牢的瞬間膛虫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工钓猬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撩独。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓敞曹,卻偏偏與公主長得像账月,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子澳迫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345